func extractFromFile(filename string) (api.BoundPod, error) { var pod api.BoundPod glog.V(3).Infof("Reading config file %q", filename) file, err := os.Open(filename) if err != nil { return pod, err } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { return pod, err } manifest := &api.ContainerManifest{} // TODO: use api.Scheme.DecodeInto if err := yaml.Unmarshal(data, manifest); err != nil { return pod, fmt.Errorf("can't unmarshal file %q: %v", filename, err) } if err := api.Scheme.Convert(manifest, &pod); err != nil { return pod, fmt.Errorf("can't convert pod from file %q: %v", filename, err) } hostname, err := os.Hostname() //TODO: kubelet name would be better if err != nil { return pod, err } if len(pod.UID) == 0 { hasher := md5.New() fmt.Fprintf(hasher, "host:%s", hostname) fmt.Fprintf(hasher, "file:%s", filename) util.DeepHashObject(hasher, pod) pod.UID = hex.EncodeToString(hasher.Sum(nil)[0:]) glog.V(5).Infof("Generated UID %q for pod %q from file %s", pod.UID, pod.Name, filename) } if len(pod.Namespace) == 0 { hasher := adler32.New() fmt.Fprint(hasher, filename) // TODO: file-<sum>.hostname would be better, if DNS subdomains // are allowed for namespace (some places only allow DNS // labels). pod.Namespace = fmt.Sprintf("file-%08x-%s", hasher.Sum32(), hostname) glog.V(5).Infof("Generated namespace %q for pod %q from file %s", pod.Namespace, pod.Name, filename) } // TODO(dchen1107): BoundPod is not type of runtime.Object. Once we allow kubelet talks // about Pod directly, we can use SelfLinker defined in package: latest // Currently just simply follow the same format in resthandler.go pod.ObjectMeta.SelfLink = fmt.Sprintf("/api/v1beta2/pods/%s?namespace=%s", pod.Name, pod.Namespace) if glog.V(4) { glog.Infof("Got pod from file %q: %#v", filename, pod) } else { glog.V(1).Infof("Got pod from file %q: %s.%s (%s)", filename, pod.Namespace, pod.Name, pod.UID) } return pod, nil }
// handleContainer handles container requests against the Kubelet. func (s *Server) handleContainer(w http.ResponseWriter, req *http.Request) { defer req.Body.Close() data, err := ioutil.ReadAll(req.Body) if err != nil { s.error(w, err) return } // This is to provide backward compatibility. It only supports a single manifest var pod api.BoundPod var containerManifest api.ContainerManifest err = yaml.Unmarshal(data, &containerManifest) if err != nil { s.error(w, err) return } pod.Name = containerManifest.ID pod.UID = containerManifest.UUID pod.Spec.Containers = containerManifest.Containers pod.Spec.Volumes = containerManifest.Volumes pod.Spec.RestartPolicy = containerManifest.RestartPolicy //TODO: sha1 of manifest? if pod.Name == "" { pod.Name = "1" } if pod.UID == "" { pod.UID = "1" } s.updates <- PodUpdate{[]api.BoundPod{pod}, SET} }
func applyDefaults(pod *api.BoundPod, url string) { if len(pod.UID) == 0 { hasher := md5.New() fmt.Fprintf(hasher, "url:%s", url) util.DeepHashObject(hasher, pod) pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:])) glog.V(5).Infof("Generated UID %q for pod %q from URL %s", pod.UID, pod.Name, url) } if len(pod.Namespace) == 0 { hasher := adler32.New() fmt.Fprint(hasher, url) pod.Namespace = fmt.Sprintf("url-%08x", hasher.Sum32()) glog.V(5).Infof("Generated namespace %q for pod %q from URL %s", pod.Namespace, pod.Name, url) } }
// newSourceApiserverFromLW holds creates a config source that watches an pulls from the apiserver. func newSourceApiserverFromLW(lw cache.ListerWatcher, updates chan<- interface{}) { send := func(objs []interface{}) { var bpods []api.BoundPod for _, o := range objs { pod := o.(*api.Pod) bpod := api.BoundPod{} if err := api.Scheme.Convert(pod, &bpod); err != nil { glog.Errorf("Unable to interpret Pod from apiserver as a BoundPod: %v: %+v", err, pod) continue } // Make a dummy self link so that references to this bound pod will work. bpod.SelfLink = "/api/v1beta1/boundPods/" + bpod.Name bpods = append(bpods, bpod) } updates <- kubelet.PodUpdate{bpods, kubelet.SET, kubelet.ApiserverSource} } cache.NewReflector(lw, &api.Pod{}, cache.NewUndeltaStore(send)).Run() }
func applyDefaults(pod *api.BoundPod, url string) { if len(pod.UID) == 0 { hasher := md5.New() fmt.Fprintf(hasher, "url:%s", url) util.DeepHashObject(hasher, pod) pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:])) glog.V(5).Infof("Generated UID %q for pod %q from URL %s", pod.UID, pod.Name, url) } // This is required for backward compatibility, and should be removed once we // completely deprecate ContainerManifest. if len(pod.Name) == 0 { pod.Name = string(pod.UID) glog.V(5).Infof("Generate Name %q from UID %q from URL %s", pod.Name, pod.UID, url) } if len(pod.Namespace) == 0 { hasher := adler32.New() fmt.Fprint(hasher, url) pod.Namespace = fmt.Sprintf("url-%08x", hasher.Sum32()) glog.V(5).Infof("Generated namespace %q for pod %q from URL %s", pod.Namespace, pod.Name, url) } }
func extractFromFile(filename string) (api.BoundPod, error) { var pod api.BoundPod glog.V(3).Infof("Reading config file %q", filename) file, err := os.Open(filename) if err != nil { return pod, err } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { return pod, err } manifest := &api.ContainerManifest{} // TODO: use api.Scheme.DecodeInto if err := yaml.Unmarshal(data, manifest); err != nil { return pod, fmt.Errorf("can't unmarshal file %q: %v", filename, err) } if err := api.Scheme.Convert(manifest, &pod); err != nil { return pod, fmt.Errorf("can't convert pod from file %q: %v", filename, err) } pod.Name = simpleSubdomainSafeHash(filename) if len(pod.UID) == 0 { pod.UID = simpleSubdomainSafeHash(filename) } if len(pod.Namespace) == 0 { pod.Namespace = api.NamespaceDefault } if glog.V(4) { glog.Infof("Got pod from file %q: %#v", filename, pod) } else { glog.V(1).Infof("Got pod from file %q: %s.%s (%s)", filename, pod.Namespace, pod.Name, pod.UID) } return pod, nil }
func extractFromFile(filename string) (api.BoundPod, error) { var pod api.BoundPod glog.V(3).Infof("Reading config file %q", filename) file, err := os.Open(filename) if err != nil { return pod, err } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { return pod, err } // TODO: use api.Scheme.DecodeInto // This is awful. DecodeInto() expects to find an APIObject, which // Manifest is not. We keep reading manifest for now for compat, but // we will eventually change it to read Pod (at which point this all // becomes nicer). Until then, we assert that the ContainerManifest // structure on disk is always v1beta1. Read that, convert it to a // "current" ContainerManifest (should be ~identical), then convert // that to a BoundPod (which is a well-understood conversion). This // avoids writing a v1beta1.ContainerManifest -> api.BoundPod // conversion which would be identical to the api.ContainerManifest -> // api.BoundPod conversion. oldManifest := &v1beta1.ContainerManifest{} if err := yaml.Unmarshal(data, oldManifest); err != nil { return pod, fmt.Errorf("can't unmarshal file %q: %v", filename, err) } newManifest := &api.ContainerManifest{} if err := api.Scheme.Convert(oldManifest, newManifest); err != nil { return pod, fmt.Errorf("can't convert pod from file %q: %v", filename, err) } if err := api.Scheme.Convert(newManifest, &pod); err != nil { return pod, fmt.Errorf("can't convert pod from file %q: %v", filename, err) } hostname, err := os.Hostname() //TODO: kubelet name would be better if err != nil { return pod, err } if len(pod.UID) == 0 { hasher := md5.New() fmt.Fprintf(hasher, "host:%s", hostname) fmt.Fprintf(hasher, "file:%s", filename) util.DeepHashObject(hasher, pod) pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:])) glog.V(5).Infof("Generated UID %q for pod %q from file %s", pod.UID, pod.Name, filename) } // This is required for backward compatibility, and should be removed once we // completely deprecate ContainerManifest. if len(pod.Name) == 0 { pod.Name = string(pod.UID) glog.V(5).Infof("Generated Name %q for UID %q from file %s", pod.Name, pod.UID, filename) } if len(pod.Namespace) == 0 { hasher := adler32.New() fmt.Fprint(hasher, filename) // TODO: file-<sum>.hostname would be better, if DNS subdomains // are allowed for namespace (some places only allow DNS // labels). pod.Namespace = fmt.Sprintf("file-%08x-%s", hasher.Sum32(), hostname) glog.V(5).Infof("Generated namespace %q for pod %q from file %s", pod.Namespace, pod.Name, filename) } // TODO(dchen1107): BoundPod is not type of runtime.Object. Once we allow kubelet talks // about Pod directly, we can use SelfLinker defined in package: latest // Currently just simply follow the same format in resthandler.go pod.ObjectMeta.SelfLink = fmt.Sprintf("/api/v1beta2/pods/%s?namespace=%s", pod.Name, pod.Namespace) if glog.V(4) { glog.Infof("Got pod from file %q: %#v", filename, pod) } else { glog.V(1).Infof("Got pod from file %q: %s.%s (%s)", filename, pod.Namespace, pod.Name, pod.UID) } return pod, nil }
func (s *sourceURL) extractFromURL() error { resp, err := http.Get(s.url) if err != nil { return err } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("%v: %v", s.url, resp.Status) } if len(data) == 0 { return fmt.Errorf("zero-length data received from %v", s.url) } // Short circuit if the manifest has not changed since the last time it was read. if bytes.Compare(data, s.data) == 0 { return nil } s.data = data // First try as if it's a single manifest var manifest api.ContainerManifest // TODO: should be api.Scheme.Decode singleErr := yaml.Unmarshal(data, &manifest) if singleErr == nil { if errs := validation.ValidateManifest(&manifest); len(errs) > 0 { singleErr = fmt.Errorf("invalid manifest: %v", errs) } } if singleErr == nil { pod := api.BoundPod{} if err := api.Scheme.Convert(&manifest, &pod); err != nil { return err } if len(pod.Name) == 0 { pod.Name = "1" } if len(pod.Namespace) == 0 { pod.Namespace = api.NamespaceDefault } s.updates <- kubelet.PodUpdate{[]api.BoundPod{pod}, kubelet.SET} return nil } // That didn't work, so try an array of manifests. var manifests []api.ContainerManifest // TODO: should be api.Scheme.Decode multiErr := yaml.Unmarshal(data, &manifests) // We're not sure if the person reading the logs is going to care about the single or // multiple manifest unmarshalling attempt, so we need to put both in the logs, as is // done at the end. Hence not returning early here. if multiErr == nil { for _, manifest := range manifests { if errs := validation.ValidateManifest(&manifest); len(errs) > 0 { multiErr = fmt.Errorf("invalid manifest: %v", errs) break } } } if multiErr == nil { // A single manifest that did not pass semantic validation will yield an empty // array of manifests (and no error) when unmarshaled as such. In that case, // if the single manifest at least had a Version, we return the single-manifest // error (if any). if len(manifests) == 0 && len(manifest.Version) != 0 { return singleErr } list := api.ContainerManifestList{Items: manifests} boundPods := &api.BoundPods{} if err := api.Scheme.Convert(&list, boundPods); err != nil { return err } for i := range boundPods.Items { pod := &boundPods.Items[i] if len(pod.Name) == 0 { pod.Name = fmt.Sprintf("%d", i+1) } if len(pod.Namespace) == 0 { pod.Namespace = api.NamespaceDefault } } s.updates <- kubelet.PodUpdate{boundPods.Items, kubelet.SET} return nil } return fmt.Errorf("%v: received '%v', but couldn't parse as a "+ "single manifest (%v: %+v) or as multiple manifests (%v: %+v).\n", s.url, string(data), singleErr, manifest, multiErr, manifests) }