func TestEnsureValidUniqueName(t *testing.T) { chars := []byte("abcdefghijk") longBytes := []byte{} for i := 0; i < (util.DNS1123SubdomainMaxLength + 20); i++ { longBytes = append(longBytes, chars[i%len(chars)]) } longName := string(longBytes) tests := []struct { name string input []string expected []string expectError bool }{ { name: "duplicate names", input: []string{"one", "two", "three", "one", "one", "two"}, expected: []string{"one", "two", "three", "one-1", "one-2", "two-1"}, }, { name: "mixed case names", input: []string{"One", "ONE", "tWo"}, expected: []string{"one", "one-1", "two"}, }, { name: "short name", input: []string{"t"}, expectError: true, }, { name: "long name", input: []string{longName, longName, longName}, expected: []string{longName[:util.DNS1123SubdomainMaxLength], namer.GetName(longName[:util.DNS1123SubdomainMaxLength], "1", util.DNS1123SubdomainMaxLength), namer.GetName(longName[:util.DNS1123SubdomainMaxLength], "2", util.DNS1123SubdomainMaxLength), }, }, } tests: for _, test := range tests { result := []string{} names := make(map[string]int) for _, i := range test.input { name, err := ensureValidUniqueName(names, i) if err != nil && !test.expectError { t.Errorf("%s: unexpected error: %v", test.name, err) } if err == nil && test.expectError { t.Errorf("%s: did not get an error.", test.name) } if err != nil { continue tests } result = append(result, name) } if !reflect.DeepEqual(result, test.expected) { t.Errorf("%s: unexpected output. Expected: %#v, Got: %#v", test.name, test.expected, result) } } }
// ensureValidName returns a new name based on the name given that is unique in // the set of names of this unique name generator. func (ung *uniqueNameGenerator) ensureValidName(name string) (string, error) { names := ung.names // Ensure that name meets length requirements if len(name) < 2 { return "", fmt.Errorf("invalid name: %s", name) } if !IsParameterizableValue(name) { // Make all names lowercase name = strings.ToLower(name) // Remove everything except [-0-9a-z] name = invalidNameCharactersRegexp.ReplaceAllString(name, "") // Remove leading hyphen(s) that may be introduced by the previous step name = strings.TrimLeft(name, "-") if len(name) > kvalidation.DNS1123SubdomainMaxLength { glog.V(4).Infof("Trimming %s to maximum allowable length (%d)\n", name, kvalidation.DNS1123SubdomainMaxLength) name = name[:kvalidation.DNS1123SubdomainMaxLength] } } count, existing := names[name] if !existing { names[name] = 0 return name, nil } count++ names[name] = count newName := namer.GetName(name, strconv.Itoa(count), kvalidation.DNS1123SubdomainMaxLength) return newName, nil }
// getNextBuildNameFromBuild returns name of the next build with random uuid added at the end func getNextBuildNameFromBuild(build *buildapi.Build) string { buildName := build.Name if matched, _ := regexp.MatchString(`^.+-\d-\d+$`, buildName); matched { nameElems := strings.Split(buildName, "-") buildName = strings.Join(nameElems[:len(nameElems)-1], "-") } suffix := fmt.Sprintf("%v", util.Now().UnixNano()) if len(suffix) > 10 { suffix = suffix[len(suffix)-10:] } return namer.GetName(buildName, suffix, util.DNS1123SubdomainMaxLength) }
// mountSecretVolume is a helper method responsible for actual mounting secret // volumes into a pod. func mountSecretVolume(pod *kapi.Pod, secretName, mountPath, volumeSuffix string) { volumeName := namer.GetName(secretName, volumeSuffix, kvalidation.DNS1123SubdomainMaxLength) volume := kapi.Volume{ Name: volumeName, VolumeSource: kapi.VolumeSource{ Secret: &kapi.SecretVolumeSource{ SecretName: secretName, }, }, } volumeMount := kapi.VolumeMount{ Name: volumeName, MountPath: mountPath, ReadOnly: true, } pod.Spec.Volumes = append(pod.Spec.Volumes, volume) pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, volumeMount) }
// getNextBuildNameFromBuild returns name of the next build with random uuid added at the end func getNextBuildNameFromBuild(build *buildapi.Build, buildConfig *buildapi.BuildConfig) string { var buildName string if buildConfig != nil { return getNextBuildName(buildConfig) } // for builds created by hand, append a timestamp when cloning/rebuilding them // because we don't have a sequence number to bump. buildName = build.Name // remove the old timestamp if we're cloning a build that is itself a clone. if matched, _ := regexp.MatchString(`^.+-\d{10}$`, buildName); matched { nameElems := strings.Split(buildName, "-") buildName = strings.Join(nameElems[:len(nameElems)-1], "-") } suffix := fmt.Sprintf("%v", unversioned.Now().UnixNano()) if len(suffix) > 10 { suffix = suffix[len(suffix)-10:] } return namer.GetName(buildName, suffix, kvalidation.DNS1123SubdomainMaxLength) }
func ensureValidUniqueName(names map[string]int, name string) (string, error) { // Ensure that name meets length requirements if len(name) < 2 { return "", fmt.Errorf("invalid name: %s", name) } if len(name) > util.DNS1123SubdomainMaxLength { glog.V(4).Infof("Trimming %s to maximum allowable length (%d)\n", name, util.DNS1123SubdomainMaxLength) name = name[:util.DNS1123SubdomainMaxLength] } // Make all names lowercase name = strings.ToLower(name) count, existing := names[name] if !existing { names[name] = 0 return name, nil } count++ names[name] = count newName := namer.GetName(name, strconv.Itoa(count), util.DNS1123SubdomainMaxLength) return newName, nil }
// DeployableContainer sets up a container for the image ready for deployment func (r *ImageRef) DeployableContainer() (container *kapi.Container, triggers []deployapi.DeploymentTriggerPolicy, err error) { name, ok := r.SuggestName() if !ok { return nil, nil, fmt.Errorf("unable to suggest a container name for the image %q", r.String()) } if r.AsImageStream { tag := r.Tag if len(tag) == 0 { tag = imageapi.DefaultImageTag } imageChangeParams := &deployapi.DeploymentTriggerImageChangeParams{ Automatic: true, ContainerNames: []string{name}, Tag: tag, } if r.Stream != nil { imageChangeParams.From = kapi.ObjectReference{ Kind: "ImageStream", Name: r.Stream.Name, Namespace: r.Stream.Namespace, } } else { imageChangeParams.From = kapi.ObjectReference{ Kind: "ImageStream", Name: name, } } triggers = []deployapi.DeploymentTriggerPolicy{ { Type: deployapi.DeploymentTriggerOnImageChange, ImageChangeParams: imageChangeParams, }, } } container = &kapi.Container{ Name: name, Image: r.String(), } // If imageInfo present, append ports if r.Info != nil && r.Info.Config != nil { ports := []string{} // ExposedPorts can consist of multiple space-separated ports for exposed := range r.Info.Config.ExposedPorts { ports = append(ports, strings.Split(exposed, " ")...) } for _, sp := range ports { p := docker.Port(sp) port, err := strconv.Atoi(p.Port()) if err != nil { return nil, nil, fmt.Errorf("failed to parse port %q: %v", p.Port(), err) } container.Ports = append(container.Ports, kapi.ContainerPort{ ContainerPort: port, Protocol: kapi.Protocol(strings.ToUpper(p.Proto())), }) } // Create volume mounts with names based on container name maxDigits := len(fmt.Sprintf("%d", len(r.Info.Config.Volumes))) baseName := namer.GetName(container.Name, volumeNameInfix, kvalidation.LabelValueMaxLength-maxDigits-1) i := 1 for volume := range r.Info.Config.Volumes { r.HasEmptyDir = true container.VolumeMounts = append(container.VolumeMounts, kapi.VolumeMount{ Name: fmt.Sprintf("%s-%d", baseName, i), ReadOnly: false, MountPath: volume, }) i++ } // TODO: Append environment variables } return container, triggers, nil }
func getTokenSecretNamePrefix(serviceAccount *api.ServiceAccount) string { return namer.GetName(serviceAccount.Name, "token-", maxSecretPrefixNameLength) }
func getDockercfgSecretNamePrefix(serviceAccount *api.ServiceAccount) string { return namer.GetName(serviceAccount.Name, "dockercfg-", maxSecretPrefixNameLength) }
// getNextBuildName returns name of the next build and increments BuildConfig's LastVersion. func getNextBuildName(buildConfig *buildapi.BuildConfig) string { buildConfig.Status.LastVersion++ return namer.GetName(buildConfig.Name, strconv.FormatInt(buildConfig.Status.LastVersion, 10), kvalidation.DNS1123SubdomainMaxLength) }
func TestUniqueNameGeneratorEnsureValidName(t *testing.T) { chars := []byte("abcdefghijk") longBytes := []byte{} for i := 0; i < (kvalidation.DNS1123SubdomainMaxLength + 20); i++ { longBytes = append(longBytes, chars[i%len(chars)]) } longName := string(longBytes) tests := []struct { name string input []string expected []string expectError bool }{ { name: "duplicate names", input: []string{"one", "two", "three", "one", "one", "two"}, expected: []string{"one", "two", "three", "one-1", "one-2", "two-1"}, }, { name: "mixed case names", input: []string{"One", "ONE", "tWo"}, expected: []string{"one", "one-1", "two"}, }, { name: "non-standard characters", input: []string{"Emby.One", "test-_test", "_-_", "@-MyRepo"}, expected: []string{"embyone", "test-test", "", "myrepo"}, }, { name: "short name", input: []string{"t"}, expectError: true, }, { name: "long name", input: []string{longName, longName, longName}, expected: []string{longName[:kvalidation.DNS1123SubdomainMaxLength], namer.GetName(longName[:kvalidation.DNS1123SubdomainMaxLength], "1", kvalidation.DNS1123SubdomainMaxLength), namer.GetName(longName[:kvalidation.DNS1123SubdomainMaxLength], "2", kvalidation.DNS1123SubdomainMaxLength), }, }, } tests: for _, test := range tests { result := []string{} nameGenerator := NewUniqueNameGenerator("").(*uniqueNameGenerator) for _, i := range test.input { name, err := nameGenerator.ensureValidName(i) if err != nil && !test.expectError { t.Errorf("%s: unexpected error: %v", test.name, err) } if err == nil && test.expectError { t.Errorf("%s: did not get an error.", test.name) } if err != nil { continue tests } result = append(result, name) } if !reflect.DeepEqual(result, test.expected) { t.Errorf("%s: unexpected output. Expected: %#v, Got: %#v", test.name, test.expected, result) } } }