-
Notifications
You must be signed in to change notification settings - Fork 38.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Makeuse of PVC namespace when provisioning gluster volumes. #34705
Makeuse of PVC namespace when provisioning gluster volumes. #34705
Conversation
Can a kubernetes member verify that this patch is reasonable to test? If so, please reply with "@k8s-bot ok to test" on its own line. Regular contributors should join the org to skip this step. |
091489c
to
0fc1655
Compare
0fc1655
to
4128f74
Compare
resturl: "http://127.0.0.1:8081" | ||
restuser: "admin" | ||
secretNamespace: "default" | ||
secretName: "heketi-secret" | ||
``` | ||
|
||
* `endpoint`: `glusterfs-cluster` is the endpoint name which includes GlusterFS trusted pool IP addresses. This parameter is mandatory. We need to also create a service for this endpoint, so that the endpoint will be persisted. This service can be without a selector to tell Kubernetes we want to add its endpoints manually. Please note that, glusterfs plugin looks for the endpoint in the pod namespace, so it is mandatory that the endpoint and service have to be created in Pod's namespace for successful mount of gluster volumes in the pod. | ||
* `endpoint`: This option is deprecated as the provisioner now create endpoint and service dynamically to support multi cluster setup. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just remove it? it is a parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
for _, a := range s.Addresses { | ||
addr[a.IP] = struct{}{} | ||
var addrlist []string | ||
if b.hosts != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add b.hosts == nil test, return error; keep rest as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
if pvSpec.Glusterfs.EndpointsName != "" { | ||
dynamicEndpoint = pvSpec.Glusterfs.EndpointsName | ||
} | ||
glog.V(1).Infof("glusterfs: dynamic endpoint and namespace : [%v/%v]", dynamicEndpoint, dynamicNamespace) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
V(1) -> V(3 or 4)
swap endpoint and namespace; usually we print namespace/endpoint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
glog.Errorf("glusterfs: error when deleting endpoint/service :%s", err) | ||
} | ||
} else { | ||
glog.V(3).Infof("glusterfs: cluster is not empty") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of logging cluster is not empty, log when the endpoint is deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return endpoint, service, nil | ||
} | ||
|
||
func (d *glusterfsVolumeDeleter) DeleteEndpointService(namespace string, epServiceName string) (err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since DeleteEndpointService
is not used outside of deleter, make it lower case deleteEndpointService
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete service first then endpoint. If endpoint is deleted then kubelet crashes, we leak a hanging service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. afaict, if we delete svc first , it will delete the ep as well. So I kept ep deletion followed by svc to make sure both are gone. Any way reverted the change and only keeping service delete.
ipaddr := dstrings.Join(nodei.NodeAddRequest.Hostnames.Storage, "") | ||
dynamicHostIps = append(dynamicHostIps, ipaddr) | ||
} | ||
glog.V(1).Infof("glusterfs: hostlist :%v", dynamicHostIps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
V(3)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
glog.V(1).Infof("glusterfs: hostlist :%v", dynamicHostIps) | ||
if len(dynamicHostIps) == 0 { | ||
glog.Errorf("glusterfs: no endpoint hosts found %s ", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no hosts found %v
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
for _, node := range clusterinfo.Nodes { | ||
nodei, err := cli.NodeInfo(string(node)) | ||
if err != nil { | ||
glog.Errorf("glusterfs: failed to get hostip %s ", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
failed to get host ip %v
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -528,20 +563,113 @@ func (p *glusterfsVolumeProvisioner) CreateVolume() (r *api.GlusterfsVolumeSourc | |||
glog.Errorf("glusterfs: failed to create gluster rest client") | |||
return nil, 0, fmt.Errorf("failed to create gluster REST client, REST server authentication failed") | |||
} | |||
volumeReq := &gapi.VolumeCreateRequest{Size: sz, Durability: gapi.VolumeDurabilityInfo{Type: durabilitytype, Replicate: gapi.ReplicaDurability{Replica: replicacount}}} | |||
volumeReq := &gapi.VolumeCreateRequest{Size: sz, Durability: gapi.VolumeDurabilityInfo{Type: durabilityType, Replicate: gapi.ReplicaDurability{Replica: replicaCount}}} | |||
volume, err := cli.VolumeCreate(volumeReq) | |||
if err != nil { | |||
glog.Errorf("glusterfs: error creating volume %s ", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error creating volume %v
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return &api.GlusterfsVolumeSource{ | ||
EndpointsName: p.endpoint, | ||
EndpointsName: endpoint.Name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't see Name in api.Endpoints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rootfs it resolves from ObjectMeta. For ex:61917ff#diff-e97253dd603331ffca81131a4b67264fR626
4128f74
to
61917ff
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//Deleter takes endpoint and endpointnamespace from pv spec. | ||
pvSpec := d.spec.Spec | ||
var dynamicEndpoint, dynamicNamespace string | ||
if pvSpec.ClaimRef.Namespace != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsafrane I don't think of any empty namespace, but is any special meaning when we get an empty namespace?
@k8s-bot gci gce e2e test this |
@childsb ^^^ |
@k8s-bot gci gke e2e test this |
@rootfs @childsb iic, it looks like, the failure is not due to the patch https://k8s-gubernator.appspot.com/build/kubernetes-jenkins/pr-logs/pull/34705/pull-kubernetes-e2e-gke-gci/102/?log . |
This looks like #33388 flake. |
Thanks @gnufied ! |
61917ff
to
b5733fb
Compare
Looks like the GCE e2e tests are stuck on this , its running for >13 hours now. |
@k8s-bot test this issue: #IGNORE |
Jenkins verification failed for commit b5733fb828fece9ab232a1182090e397f9559491. Full PR test history. The magic incantation to run this job again is |
b5733fb
to
da4f713
Compare
@k8s-bot test this issue: #IGNORE |
var addrlist []string | ||
if b.hosts == nil { | ||
return fmt.Errorf("glusterfs: endpoint is nil") | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you do not need extra else
branch after return
above, you ca save yourself one indentation level.
if b.hosts == nil { | ||
return fmt.Errorf("glusterfs: endpoint is nil") | ||
} else { | ||
addr := make(map[string]struct{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addr
seems not used, it's filled with addresses, is it read anywhere?
|
||
// Avoid mount storm, pick a host randomly. | ||
// Iterate all hosts until mount succeeds. | ||
for _, ip := range addrlist { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"pick a host randomly" - the code above does not look too much random
var dynamicEndpoint, dynamicNamespace string | ||
if pvSpec.ClaimRef.Namespace != "" { | ||
dynamicNamespace = pvSpec.ClaimRef.Namespace | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please check if pvSpec.ClaimRef is nil
(and throw an error if it is) and throw an error when ClaimRef.Namespace
is empty
} | ||
_, err = p.plugin.host.GetKubeClient().Core().Endpoints(namespace).Create(endpoint) | ||
if err != nil && errors.IsAlreadyExists(err) { | ||
glog.V(1).Infof("glusterfs: endpoint %s already exist", endpoint) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please log also namespace when logging endpoint or service name (this applies to most of logging below)
} | ||
glog.V(3).Infof("glusterfs: dynamic namespace and endpoint : [%v/%v]", dynamicNamespace, dynamicEndpoint) | ||
//If there are no volumes exist in the cluster, the endpoint and svc | ||
//will be deleted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is suspicious. Let there be two namespaces, nsA and nsB, both with one dynamically created endpoint, nsA/ep1 (used by pv1) and nsB/ep2 (used by pv2). Now, when pv1 is deleted, heketi returns that there is still a volume (pv2) in the cluster and nsA/ep1 is not deleted. Subsequently, when pv2 is deleted, heketi cluster is empty and ep2 is deleted.
ep1 is never deleted. Who/when is supposed to delete it? You should delete all these stray endpoints in all namespaces.
Also, at the time you check len(clusterinfo.Volumes)
below, there may already be a new volume just created by another provisioning goroutine.
IMO, the safest (and race free) way is to create an endpoint/service for each provisioned PV. When you delete a PV, you always delete associated endpoint + service.
It could be possible to share one EP + service in a namespace for all PVs, however you need to be extra careful not to delete it in corner cases when a new PV is being provisioned and some old PV is being deleted at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jsafrane for the review. Reg# creating an ep/svc for each PV, It was discussed that, it may be a tedious approach. Reg# the stale EPs in the some of the Namespaces, even today once the admin create an endpoint in one or more NS, he has to manually delete the ep/svc if not required. I am open to take any path though :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way, endpoints/services are deleted from some namespaces, while they are kept in others. It's IMO very bad user experience, it should behave consistently and it should be well documented.
There are minor issues I can live with (logging, claimref check), however this PR will most probably create endpoints in user namespaces that nobody deletes when they are not needed. I'd rather have it done right instead of patching it later. |
da4f713
to
5a60a5d
Compare
@k8s-bot test this issue: #IGNORE |
Jenkins unit/integration failed for commit 5a60a5da2005fcc854210caa28eee5cd22db2de9. Full PR test history. The magic incantation to run this job again is |
5a60a5d
to
7f9faef
Compare
@k8s-bot test this issue: #IGNORE |
Jenkins GCE etcd3 e2e failed for commit 7f9faef. Full PR test history. The magic incantation to run this job again is |
glog.Errorf("glusterfs: failed to delete service %s in namespace %s error %v", epServiceName, namespace, err) | ||
return fmt.Errorf("error deleting service %v in namespace [%s]", err, namespace) | ||
} | ||
glog.V(1).Infof("glusterfs: service/endpoint [%s] in namespace [%s] deleted successfully", epServiceName, namespace) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we delete also the endpoint here? I know, it may be garbage collected automatically, however we created it, we should delete it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@humblec @jsafrane removing service first can survive a sudden crash without leaking endpoint, since hanging endpoint is going to be cleaned up. But I would suggest removing endpoint as well - in case next time the endpoint is to be created and provisioner hit the line and prematurely exit without creating service.
_, err = p.plugin.host.GetKubeClient().Core().Endpoints(namespace).Create(endpoint)
if err != nil && errors.IsAlreadyExists(err)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rootfs if we delete the svc first, the ep is gone at the same moment and the very next line which is delete endpoint give error saying non existence of ep. so I kept only svc deletion. Also when we delete svc from commandline, the ep is deleted internally. afaict, we only need to delete svc.
return nil, nil, fmt.Errorf("error creating endpoint %v", err) | ||
} | ||
service = &api.Service{ | ||
ObjectMeta: api.ObjectMeta{Name: epServiceName, Namespace: namespace}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add gluster.kubernetes.io/provisioned-for-pvc
label to Service
return nil, 0, fmt.Errorf("no hosts found %v", err) | ||
} | ||
|
||
// The 'endpointname' is created in form of 'cluster-<id>' where 'id' is the cluster id. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cluster-<id>
is no longer used
Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
7f9faef
to
0d080f9
Compare
@k8s-bot test this issue: #IGNORE |
lgtm |
Jenkins GCI GKE smoke e2e failed for commit 0d080f9. Full PR test history. The magic incantation to run this job again is |
Automatic merge from submit-queue |
Depends on #34611
This change is