Example #1
0
// 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
}
Example #2
0
// 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
}
Example #3
0
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)
}
Example #4
0
// 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)
}
Example #5
0
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
	}
}
Example #6
0
// 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
}
Example #7
0
// 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
}
Example #8
0
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)
		}
	}
}
Example #9
0
// 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
}
Example #10
0
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)
		},
	)
}