// PullImage invokes 'rkt fetch' to download an aci. // TODO(yifan): Now we only support docker images, this should be changed // once the format of image is landed, see: // // http://issue.k8s.io/7203 // func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Secret) error { img := image.Image // TODO(yifan): The credential operation is a copy from dockertools package, // Need to resolve the code duplication. repoToPull, _ := parsers.ParseImageName(img) keyring, err := credentialprovider.MakeDockerKeyring(pullSecrets, r.dockerKeyring) if err != nil { return err } creds, ok := keyring.Lookup(repoToPull) if !ok { glog.V(1).Infof("Pulling image %s without credentials", img) } // Let's update a json. // TODO(yifan): Find a way to feed this to rkt. if err := r.writeDockerAuthConfig(img, creds); err != nil { return err } if _, err := r.runCommand("fetch", dockerPrefix+img); err != nil { glog.Errorf("Failed to fetch: %v", err) return err } return nil }
// listImages lists the images that have the given name. If detail is true, // then image manifest is also included in the result. // Note that there could be more than one images that have the given name, we // will return the result reversely sorted by the import time, so that the latest // image comes first. func (r *Runtime) listImages(image string, detail bool) ([]*rktapi.Image, error) { repoToPull, tag, _, err := parsers.ParseImageName(image) if err != nil { return nil, err } listResp, err := r.apisvc.ListImages(context.Background(), &rktapi.ListImagesRequest{ Detail: detail, Filters: []*rktapi.ImageFilter{ { // TODO(yifan): Add a field in the ImageFilter to match the whole name, // not just keywords. // https://github.com/coreos/rkt/issues/1872#issuecomment-166456938 Keywords: []string{repoToPull}, Labels: []*rktapi.KeyValue{{Key: "version", Value: tag}}, }, }, }) if err != nil { return nil, fmt.Errorf("couldn't list images: %v", err) } // TODO(yifan): Let the API service to sort the result: // See https://github.com/coreos/rkt/issues/1911. sort.Sort(sort.Reverse(sortByImportTime(listResp.Images))) return listResp.Images, nil }
func (p dockerPuller) Pull(image string, secrets []api.Secret) error { // If no tag was specified, use the default "latest". imageID, tag, err := parsers.ParseImageName(image) if err != nil { return err } keyring, err := credentialprovider.MakeDockerKeyring(secrets, p.keyring) if err != nil { return err } opts := dockertypes.ImagePullOptions{ Tag: tag, } creds, haveCredentials := keyring.Lookup(imageID) if !haveCredentials { glog.V(1).Infof("Pulling image %s without credentials", image) err := p.client.PullImage(imageID, dockertypes.AuthConfig{}, opts) if err == nil { // Sometimes PullImage failed with no error returned. exist, ierr := p.IsImagePresent(image) if ierr != nil { glog.Warningf("Failed to inspect image %s: %v", image, ierr) } if !exist { return fmt.Errorf("image pull failed for unknown error") } return nil } // Image spec: [<registry>/]<repository>/<image>[:<version] so we count '/' explicitRegistry := (strings.Count(image, "/") == 2) // Hack, look for a private registry, and decorate the error with the lack of // credentials. This is heuristic, and really probably could be done better // by talking to the registry API directly from the kubelet here. if explicitRegistry { return fmt.Errorf("image pull failed for %s, this may be because there are no credentials on this request. details: (%v)", image, err) } return filterHTTPError(err, image) } var pullErrs []error for _, currentCreds := range creds { err = p.client.PullImage(imageID, credentialprovider.LazyProvide(currentCreds), opts) // If there was no error, return success if err == nil { return nil } pullErrs = append(pullErrs, filterHTTPError(err, image)) } return utilerrors.NewAggregate(pullErrs) }
// PullImage pulls an image from the network to local storage using the supplied // secrets if necessary. // TODO: pull image with qps and burst, ref https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/dockertools/docker.go#L120 func (m *kubeGenericRuntimeManager) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Secret) error { img := image.Image repoToPull, _, _, err := parsers.ParseImageName(img) if err != nil { return err } keyring, err := credentialprovider.MakeDockerKeyring(pullSecrets, m.keyring) if err != nil { return err } imgSpec := &runtimeApi.ImageSpec{Image: &img} creds, withCredentials := keyring.Lookup(repoToPull) if !withCredentials { glog.V(3).Infof("Pulling image %q without credentials", img) err = m.imageService.PullImage(imgSpec, nil) if err != nil { glog.Errorf("Pull image %q failed: %v", img, err) return err } return nil } var pullErrs []error for _, currentCreds := range creds { authConfig := credentialprovider.LazyProvide(currentCreds) auth := &runtimeApi.AuthConfig{ Username: &authConfig.Username, Password: &authConfig.Password, Auth: &authConfig.Auth, ServerAddress: &authConfig.ServerAddress, IdentityToken: &authConfig.IdentityToken, RegistryToken: &authConfig.RegistryToken, } err = m.imageService.PullImage(imgSpec, auth) // If there was no error, return success if err == nil { return nil } pullErrs = append(pullErrs, err) } return utilerrors.NewAggregate(pullErrs) }
func SetDefaults_Container(obj *Container) { if obj.ImagePullPolicy == "" { // Ignore error and assume it has been validated elsewhere _, tag, _, _ := parsers.ParseImageName(obj.Image) // Check image tag if tag == "latest" { obj.ImagePullPolicy = PullAlways } else { obj.ImagePullPolicy = PullIfNotPresent } } if obj.TerminationMessagePath == "" { obj.TerminationMessagePath = TerminationMessagePathDefault } }
// listImages lists the images that have the given name. If detail is true, // then image manifest is also included in the result. // Note that there could be more than one images that have the given name, we // will return the result reversely sorted by the import time, so that the latest // image comes first. func (r *Runtime) listImages(image string, detail bool) ([]*rktapi.Image, error) { repoToPull, tag, _, err := parsers.ParseImageName(image) if err != nil { return nil, err } imageFilters := []*rktapi.ImageFilter{ { // TODO(yifan): Add a field in the ImageFilter to match the whole name, // not just keywords. // https://github.com/coreos/rkt/issues/1872#issuecomment-166456938 Keywords: []string{repoToPull}, Labels: []*rktapi.KeyValue{{Key: "version", Value: tag}}, }, } // If the repo name is not a valid ACIdentifier (namely if it has a port), // then it will have a different name in the store. Search for both the // original name and this modified name in case we choose to also change the // api-service to do this un-conversion on its end. if appcRepoToPull, err := appctypes.SanitizeACIdentifier(repoToPull); err != nil { glog.Warningf("could not convert %v to an aci identifier: %v", err) } else { imageFilters = append(imageFilters, &rktapi.ImageFilter{ Keywords: []string{appcRepoToPull}, Labels: []*rktapi.KeyValue{{Key: "version", Value: tag}}, }) } ctx, cancel := context.WithTimeout(context.Background(), r.requestTimeout) defer cancel() listResp, err := r.apisvc.ListImages(ctx, &rktapi.ListImagesRequest{ Detail: detail, Filters: imageFilters, }) if err != nil { return nil, fmt.Errorf("couldn't list images: %v", err) } // TODO(yifan): Let the API service to sort the result: // See https://github.com/coreos/rkt/issues/1911. sort.Sort(sort.Reverse(sortByImportTime(listResp.Images))) return listResp.Images, nil }
// PullImage invokes 'rkt fetch' to download an aci. // TODO(yifan): Now we only support docker images, this should be changed // once the format of image is landed, see: // // http://issue.k8s.io/7203 // func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Secret) error { img := image.Image // TODO(yifan): The credential operation is a copy from dockertools package, // Need to resolve the code duplication. repoToPull, _, _, err := parsers.ParseImageName(img) if err != nil { return err } keyring, err := credentialprovider.MakeDockerKeyring(pullSecrets, r.dockerKeyring) if err != nil { return err } creds, ok := keyring.Lookup(repoToPull) if !ok { glog.V(1).Infof("Pulling image %s without credentials", img) } userConfigDir, err := ioutil.TempDir("", "rktnetes-user-config-dir-") if err != nil { return fmt.Errorf("rkt: Cannot create a temporary user-config directory: %v", err) } defer os.RemoveAll(userConfigDir) config := *r.config config.UserConfigDir = userConfigDir if err := r.writeDockerAuthConfig(img, creds, userConfigDir); err != nil { return err } // Today, `--no-store` will fetch the remote image regardless of whether the content of the image // has changed or not. This causes performance downgrades when the image tag is ':latest' and // the image pull policy is 'always'. The issue is tracked in https://github.com/coreos/rkt/issues/2937. if _, err := r.cli.RunCommand(&config, "fetch", "--no-store", dockerPrefix+img); err != nil { glog.Errorf("Failed to fetch: %v", err) return err } return nil }
func TestParseImageName(t *testing.T) { tests := []struct { imageName string name string tag string }{ {"ubuntu", "ubuntu", "latest"}, {"ubuntu:2342", "ubuntu", "2342"}, {"ubuntu:latest", "ubuntu", "latest"}, {"foo/bar:445566", "foo/bar", "445566"}, {"registry.example.com:5000/foobar", "registry.example.com:5000/foobar", "latest"}, {"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar", "5342"}, {"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar", "latest"}, } for _, test := range tests { name, tag := parsers.ParseImageName(test.imageName) if name != test.name || tag != test.tag { t.Errorf("Expected name/tag: %s/%s, got %s/%s", test.name, test.tag, name, tag) } } }
// PullImage invokes 'rkt fetch' to download an aci. // TODO(yifan): Now we only support docker images, this should be changed // once the format of image is landed, see: // // http://issue.k8s.io/7203 // func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Secret) error { img := image.Image // TODO(yifan): The credential operation is a copy from dockertools package, // Need to resolve the code duplication. repoToPull, _, _, err := parsers.ParseImageName(img) if err != nil { return err } keyring, err := credentialprovider.MakeDockerKeyring(pullSecrets, r.dockerKeyring) if err != nil { return err } creds, ok := keyring.Lookup(repoToPull) if !ok { glog.V(1).Infof("Pulling image %s without credentials", img) } userConfigDir, err := ioutil.TempDir("", "rktnetes-user-config-dir-") if err != nil { return fmt.Errorf("rkt: Cannot create a temporary user-config directory: %v", err) } defer os.RemoveAll(userConfigDir) config := *r.config config.UserConfigDir = userConfigDir if err := r.writeDockerAuthConfig(img, creds, userConfigDir); err != nil { return err } if _, err := r.cli.RunCommand(&config, "fetch", dockerPrefix+img); err != nil { glog.Errorf("Failed to fetch: %v", err) return err } return nil }
func addDefaultingFuncs(scheme *runtime.Scheme) { scheme.AddDefaultingFuncs( func(obj *PodExecOptions) { obj.Stdout = true obj.Stderr = true }, func(obj *PodAttachOptions) { obj.Stdout = true obj.Stderr = true }, func(obj *ReplicationController) { var labels map[string]string if obj.Spec.Template != nil { labels = obj.Spec.Template.Labels } // TODO: support templates defined elsewhere when we support them in the API if labels != nil { if len(obj.Spec.Selector) == 0 { obj.Spec.Selector = labels } if len(obj.Labels) == 0 { obj.Labels = labels } } if obj.Spec.Replicas == nil { obj.Spec.Replicas = new(int32) *obj.Spec.Replicas = 1 } }, func(obj *Volume) { if util.AllPtrFieldsNil(&obj.VolumeSource) { obj.VolumeSource = VolumeSource{ EmptyDir: &EmptyDirVolumeSource{}, } } }, func(obj *ContainerPort) { if obj.Protocol == "" { obj.Protocol = ProtocolTCP } // Carry conversion to make port case valid switch strings.ToUpper(string(obj.Protocol)) { case string(ProtocolTCP): obj.Protocol = ProtocolTCP case string(ProtocolUDP): obj.Protocol = ProtocolUDP } }, func(obj *Container) { if obj.ImagePullPolicy == "" { _, tag := parsers.ParseImageName(obj.Image) // Check image tag if tag == "latest" { obj.ImagePullPolicy = PullAlways } else { obj.ImagePullPolicy = PullIfNotPresent } } if obj.TerminationMessagePath == "" { obj.TerminationMessagePath = TerminationMessagePathDefault } }, func(obj *ServiceSpec) { if obj.SessionAffinity == "" { obj.SessionAffinity = ServiceAffinityNone } if obj.Type == "" { obj.Type = ServiceTypeClusterIP } for i := range obj.Ports { sp := &obj.Ports[i] if sp.Protocol == "" { sp.Protocol = ProtocolTCP } if sp.TargetPort == intstr.FromInt(0) || sp.TargetPort == intstr.FromString("") { sp.TargetPort = intstr.FromInt(int(sp.Port)) } } // Carry conversion if len(obj.ClusterIP) == 0 && len(obj.DeprecatedPortalIP) > 0 { obj.ClusterIP = obj.DeprecatedPortalIP } }, func(obj *ServicePort) { // Carry conversion to make port case valid switch strings.ToUpper(string(obj.Protocol)) { case string(ProtocolTCP): obj.Protocol = ProtocolTCP case string(ProtocolUDP): obj.Protocol = ProtocolUDP } }, func(obj *Pod) { // If limits are specified, but requests are not, default requests to limits // This is done here rather than a more specific defaulting pass on ResourceRequirements // because we only want this defaulting semantic to take place on a Pod and not a PodTemplate for i := range obj.Spec.Containers { // set requests to limits if requests are not specified, but limits are if obj.Spec.Containers[i].Resources.Limits != nil { if obj.Spec.Containers[i].Resources.Requests == nil { obj.Spec.Containers[i].Resources.Requests = make(ResourceList) } for key, value := range obj.Spec.Containers[i].Resources.Limits { if _, exists := obj.Spec.Containers[i].Resources.Requests[key]; !exists { obj.Spec.Containers[i].Resources.Requests[key] = *(value.Copy()) } } } } }, func(obj *PodSpec) { if obj.DNSPolicy == "" { obj.DNSPolicy = DNSClusterFirst } if obj.RestartPolicy == "" { obj.RestartPolicy = RestartPolicyAlways } if obj.HostNetwork { defaultHostNetworkPorts(&obj.Containers) } if obj.SecurityContext == nil { obj.SecurityContext = &PodSecurityContext{} } // Carry migration from serviceAccount to serviceAccountName if len(obj.ServiceAccountName) == 0 && len(obj.DeprecatedServiceAccount) > 0 { obj.ServiceAccountName = obj.DeprecatedServiceAccount } // Carry migration from host to nodeName if len(obj.NodeName) == 0 && len(obj.DeprecatedHost) > 0 { obj.NodeName = obj.DeprecatedHost } if obj.TerminationGracePeriodSeconds == nil { period := int64(DefaultTerminationGracePeriodSeconds) obj.TerminationGracePeriodSeconds = &period } }, func(obj *Probe) { if obj.TimeoutSeconds == 0 { obj.TimeoutSeconds = 1 } if obj.PeriodSeconds == 0 { obj.PeriodSeconds = 10 } if obj.SuccessThreshold == 0 { obj.SuccessThreshold = 1 } if obj.FailureThreshold == 0 { obj.FailureThreshold = 3 } }, func(obj *Secret) { if obj.Type == "" { obj.Type = SecretTypeOpaque } }, func(obj *PersistentVolume) { if obj.Status.Phase == "" { obj.Status.Phase = VolumePending } if obj.Spec.PersistentVolumeReclaimPolicy == "" { obj.Spec.PersistentVolumeReclaimPolicy = PersistentVolumeReclaimRetain } }, func(obj *PersistentVolumeClaim) { if obj.Status.Phase == "" { obj.Status.Phase = ClaimPending } }, func(obj *ISCSIVolumeSource) { if obj.ISCSIInterface == "" { obj.ISCSIInterface = "default" } }, func(obj *Endpoints) { for i := range obj.Subsets { ss := &obj.Subsets[i] for i := range ss.Ports { ep := &ss.Ports[i] if ep.Protocol == "" { ep.Protocol = ProtocolTCP } } } }, func(obj *EndpointPort) { // Carry conversion to make port case valid switch strings.ToUpper(string(obj.Protocol)) { case string(ProtocolTCP): obj.Protocol = ProtocolTCP case string(ProtocolUDP): obj.Protocol = ProtocolUDP } }, func(obj *HTTPGetAction) { if obj.Path == "" { obj.Path = "/" } if obj.Scheme == "" { obj.Scheme = URISchemeHTTP } }, func(obj *NamespaceStatus) { if obj.Phase == "" { obj.Phase = NamespaceActive } }, func(obj *Node) { if obj.Spec.ExternalID == "" { obj.Spec.ExternalID = obj.Name } }, func(obj *NodeStatus) { if obj.Allocatable == nil && obj.Capacity != nil { obj.Allocatable = make(ResourceList, len(obj.Capacity)) for key, value := range obj.Capacity { obj.Allocatable[key] = *(value.Copy()) } obj.Allocatable = obj.Capacity } }, func(obj *ObjectFieldSelector) { if obj.APIVersion == "" { obj.APIVersion = "v1" } }, func(obj *LimitRangeItem) { // for container limits, we apply default values if obj.Type == LimitTypeContainer { if obj.Default == nil { obj.Default = make(ResourceList) } if obj.DefaultRequest == nil { obj.DefaultRequest = make(ResourceList) } // If a default limit is unspecified, but the max is specified, default the limit to the max for key, value := range obj.Max { if _, exists := obj.Default[key]; !exists { obj.Default[key] = *(value.Copy()) } } // If a default limit is specified, but the default request is not, default request to limit for key, value := range obj.Default { if _, exists := obj.DefaultRequest[key]; !exists { obj.DefaultRequest[key] = *(value.Copy()) } } // If a default request is not specified, but the min is provided, default request to the min for key, value := range obj.Min { if _, exists := obj.DefaultRequest[key]; !exists { obj.DefaultRequest[key] = *(value.Copy()) } } } }, func(obj *ConfigMap) { if obj.Data == nil { obj.Data = make(map[string]string) } }, func(obj *SecurityContextConstraints) { defaultSecurityContextConstraints(obj) }, ) }