func (tw *baseTimeoutWriter) timeout(msg string) { tw.mu.Lock() defer tw.mu.Unlock() tw.timedOut = true // The timeout writer has not been used by the inner handler. // We can safely timeout the HTTP request by sending by a timeout // handler if !tw.wroteHeader && !tw.hijacked { tw.w.WriteHeader(http.StatusGatewayTimeout) if msg != "" { tw.w.Write([]byte(msg)) } else { enc := json.NewEncoder(tw.w) enc.Encode(errors.NewServerTimeout(api.Resource(""), "", 0)) } } else { // The timeout writer has been used by the inner handler. There is // no way to timeout the HTTP request at the point. We have to shutdown // the connection for HTTP1 or reset stream for HTTP2. // // Note from: Brad Fitzpatrick // if the ServeHTTP goroutine panics, that will do the best possible thing for both // HTTP/1 and HTTP/2. In HTTP/1, assuming you're replying with at least HTTP/1.1 and // you've already flushed the headers so it's using HTTP chunking, it'll kill the TCP // connection immediately without a proper 0-byte EOF chunk, so the peer will recognize // the response as bogus. In HTTP/2 the server will just RST_STREAM the stream, leaving // the TCP connection open, but resetting the stream to the peer so it'll have an error, // like the HTTP/1 case. panic(errConnKilled) } }
// InterpretCreateError converts a generic error on a create // operation into the appropriate API error. func InterpretCreateError(err error, qualifiedResource schema.GroupResource, name string) error { switch { case storage.IsNodeExist(err): return errors.NewAlreadyExists(qualifiedResource, name) case storage.IsUnreachable(err): return errors.NewServerTimeout(qualifiedResource, "create", 2) // TODO: make configurable or handled at a higher level default: return err } }
// InterpretGetError converts a generic error on a retrieval // operation into the appropriate API error. func InterpretGetError(err error, qualifiedResource schema.GroupResource, name string) error { switch { case storage.IsNotFound(err): return errors.NewNotFound(qualifiedResource, name) case storage.IsUnreachable(err): return errors.NewServerTimeout(qualifiedResource, "get", 2) // TODO: make configurable or handled at a higher level default: return err } }
// InterpretDeleteError converts a generic error on a delete // operation into the appropriate API error. func InterpretDeleteError(err error, qualifiedResource schema.GroupResource, name string) error { switch { case storage.IsNotFound(err): return errors.NewNotFound(qualifiedResource, name) case storage.IsUnreachable(err): return errors.NewServerTimeout(qualifiedResource, "delete", 2) // TODO: make configurable or handled at a higher level case storage.IsConflict(err), storage.IsNodeExist(err), storage.IsInvalidObj(err): return errors.NewConflict(qualifiedResource, name, err) case storage.IsInternalError(err): return errors.NewInternalError(err) default: return err } }
func (s *serviceAccount) mountServiceAccountToken(serviceAccount *api.ServiceAccount, pod *api.Pod) error { // Find the name of a referenced ServiceAccountToken secret we can mount serviceAccountToken, err := s.getReferencedServiceAccountToken(serviceAccount) if err != nil { return fmt.Errorf("Error looking up service account token for %s/%s: %v", serviceAccount.Namespace, serviceAccount.Name, err) } if len(serviceAccountToken) == 0 { // We don't have an API token to mount, so return if s.RequireAPIToken { // If a token is required, this is considered an error err := errors.NewServerTimeout(schema.GroupResource{Resource: "serviceaccounts"}, "create pod", 1) err.ErrStatus.Message = fmt.Sprintf("No API token found for service account %q, retry after the token is automatically created and added to the service account", serviceAccount.Name) return err } return nil } // Find the volume and volume name for the ServiceAccountTokenSecret if it already exists tokenVolumeName := "" hasTokenVolume := false allVolumeNames := sets.NewString() for _, volume := range pod.Spec.Volumes { allVolumeNames.Insert(volume.Name) if volume.Secret != nil && volume.Secret.SecretName == serviceAccountToken { tokenVolumeName = volume.Name hasTokenVolume = true break } } // Determine a volume name for the ServiceAccountTokenSecret in case we need it if len(tokenVolumeName) == 0 { // Try naming the volume the same as the serviceAccountToken, and uniquify if needed tokenVolumeName = serviceAccountToken if allVolumeNames.Has(tokenVolumeName) { tokenVolumeName = names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-", serviceAccountToken)) } } // Create the prototypical VolumeMount volumeMount := api.VolumeMount{ Name: tokenVolumeName, ReadOnly: true, MountPath: DefaultAPITokenMountPath, } // Ensure every container mounts the APISecret volume needsTokenVolume := false for i, container := range pod.Spec.InitContainers { existingContainerMount := false for _, volumeMount := range container.VolumeMounts { // Existing mounts at the default mount path prevent mounting of the API token if volumeMount.MountPath == DefaultAPITokenMountPath { existingContainerMount = true break } } if !existingContainerMount { pod.Spec.InitContainers[i].VolumeMounts = append(pod.Spec.InitContainers[i].VolumeMounts, volumeMount) needsTokenVolume = true } } for i, container := range pod.Spec.Containers { existingContainerMount := false for _, volumeMount := range container.VolumeMounts { // Existing mounts at the default mount path prevent mounting of the API token if volumeMount.MountPath == DefaultAPITokenMountPath { existingContainerMount = true break } } if !existingContainerMount { pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts, volumeMount) needsTokenVolume = true } } // Add the volume if a container needs it if !hasTokenVolume && needsTokenVolume { volume := api.Volume{ Name: tokenVolumeName, VolumeSource: api.VolumeSource{ Secret: &api.SecretVolumeSource{ SecretName: serviceAccountToken, }, }, } pod.Spec.Volumes = append(pod.Spec.Volumes, volume) } return nil }