// RemoteURL returns the remote URL of the source repository func (r *SourceRepository) RemoteURL() (*url.URL, bool, error) { if r.remoteURL != nil { return r.remoteURL, true, nil } switch r.url.Scheme { case "file": gitRepo := git.NewRepository() remote, ok, err := gitRepo.GetOriginURL(r.url.Path) if err != nil && err != git.ErrGitNotAvailable { return nil, false, err } if !ok { return nil, ok, nil } ref := gitRepo.GetRef(r.url.Path) if len(ref) > 0 { remote = fmt.Sprintf("%s#%s", remote, ref) } if r.remoteURL, err = git.ParseRepository(remote); err != nil { return nil, false, err } default: r.remoteURL = &r.url } return r.remoteURL, true, nil }
// run is responsible for preparing environment for actual build. // It accepts factoryFunc and an ordered array of SCMAuths. func run(builderFactory factoryFunc) { client, endpoint, err := dockerutil.NewHelper().GetClient() if err != nil { glog.Fatalf("Error obtaining docker client: %v", err) } buildStr := os.Getenv("BUILD") glog.V(4).Infof("$BUILD env var is %s \n", buildStr) build := api.Build{} if err := latest.Codec.DecodeInto([]byte(buildStr), &build); err != nil { glog.Fatalf("Unable to parse build: %v", err) } if build.Spec.Source.SourceSecret != nil { sourceURL, err := git.ParseRepository(build.Spec.Source.Git.URI) if err != nil { glog.Fatalf("Cannot parse build URL: %s", build.Spec.Source.Git.URI) } scmAuths := auths(sourceURL) if err := setupSourceSecret(build.Spec.Source.SourceSecret.Name, scmAuths); err != nil { glog.Fatalf("Cannot setup secret file for accessing private repository: %v", err) } } b := builderFactory(client, endpoint, &build) if err = b.Build(); err != nil { glog.Fatalf("Build error: %v", err) } if build.Spec.Output.To == nil || len(build.Spec.Output.To.Name) == 0 { glog.Warning("Build does not have an Output defined, no output image was pushed to a registry.") } }
// NewSourceRepository creates a reference to a local or remote source code repository from // a URL or path. func NewSourceRepository(s string) (*SourceRepository, error) { location, err := git.ParseRepository(s) if err != nil { return nil, err } return &SourceRepository{ location: s, url: *location, }, nil }
// NewSourceRepository creates a reference to a local or remote source code repository from // a URL or path. func NewSourceRepository(s string, strategy generate.Strategy) (*SourceRepository, error) { location, err := git.ParseRepository(s) if err != nil { return nil, err } return &SourceRepository{ location: s, url: *location, strategy: strategy, }, nil }
func (c *builderConfig) setupGitEnvironment() (string, []string, error) { var sourceSecretDir string var errSecret error // For now, we only handle git. If not specified, we're done gitSource := c.build.Spec.Source.Git if gitSource == nil { return "", []string{}, nil } sourceSecret := c.build.Spec.Source.SourceSecret gitEnv := []string{"GIT_ASKPASS=true"} // If a source secret is present, set it up and add its environment variables if sourceSecret != nil { // TODO: this should be refactored to let each source type manage which secrets // it accepts sourceURL, err := git.ParseRepository(gitSource.URI) if err != nil { return "", nil, fmt.Errorf("cannot parse build URL: %s", gitSource.URI) } scmAuths := scmauth.GitAuths(sourceURL) // TODO: remove when not necessary to fix up the secret dir permission sourceSecretDir, errSecret = fixSecretPermissions(c.sourceSecretDir) if errSecret != nil { return sourceSecretDir, nil, fmt.Errorf("cannot fix source secret permissions: %v", errSecret) } secretsEnv, overrideURL, err := scmAuths.Setup(sourceSecretDir) if err != nil { return sourceSecretDir, nil, fmt.Errorf("cannot setup source secret: %v", err) } if overrideURL != nil { c.build.Annotations[bld.OriginalSourceURLAnnotationKey] = gitSource.URI gitSource.URI = overrideURL.String() } gitEnv = append(gitEnv, secretsEnv...) } if gitSource.HTTPProxy != nil && len(*gitSource.HTTPProxy) > 0 { gitEnv = append(gitEnv, fmt.Sprintf("HTTP_PROXY=%s", *gitSource.HTTPProxy)) gitEnv = append(gitEnv, fmt.Sprintf("http_proxy=%s", *gitSource.HTTPProxy)) } if gitSource.HTTPSProxy != nil && len(*gitSource.HTTPSProxy) > 0 { gitEnv = append(gitEnv, fmt.Sprintf("HTTPS_PROXY=%s", *gitSource.HTTPSProxy)) gitEnv = append(gitEnv, fmt.Sprintf("https_proxy=%s", *gitSource.HTTPSProxy)) } if gitSource.NoProxy != nil && len(*gitSource.NoProxy) > 0 { gitEnv = append(gitEnv, fmt.Sprintf("NO_PROXY=%s", *gitSource.NoProxy)) gitEnv = append(gitEnv, fmt.Sprintf("no_proxy=%s", *gitSource.NoProxy)) } return sourceSecretDir, bld.MergeEnv(os.Environ(), gitEnv), nil }
// run is responsible for preparing environment for actual build. // It accepts factoryFunc and an ordered array of SCMAuths. func run(b builder) { dockerClient, endpoint, err := dockerutil.NewHelper().GetClient() if err != nil { glog.Fatalf("Error obtaining docker client: %v", err) } buildStr := os.Getenv("BUILD") glog.V(4).Infof("$BUILD env var is %s \n", buildStr) build := api.Build{} if err := latest.Codec.DecodeInto([]byte(buildStr), &build); err != nil { glog.Fatalf("Unable to parse build: %v", err) } if build.Spec.Source.SourceSecret != nil { if build.Spec.Source.Git != nil { // TODO: this should be refactored to let each source type manage which secrets // it accepts sourceURL, err := git.ParseRepository(build.Spec.Source.Git.URI) if err != nil { glog.Fatalf("Cannot parse build URL: %s", build.Spec.Source.Git.URI) } scmAuths := auths(sourceURL) sourceURL, err = setupSourceSecret(build.Spec.Source.SourceSecret.Name, scmAuths) if err != nil { glog.Fatalf("Cannot setup secret file for accessing private repository: %v", err) } if sourceURL != nil { build.Annotations[bld.OriginalSourceURLAnnotationKey] = build.Spec.Source.Git.URI build.Spec.Source.Git.URI = sourceURL.String() } } } config, err := kclient.InClusterConfig() if err != nil { glog.Fatalf("Failed to get client config: %v", err) } osClient, err := client.New(config) if err != nil { glog.Fatalf("Error obtaining OpenShift client: %v", err) } buildsClient := osClient.Builds(build.Namespace) if err = b.Build(dockerClient, endpoint, buildsClient, &build); err != nil { glog.Fatalf("Build error: %v", err) } if build.Spec.Output.To == nil || len(build.Spec.Output.To.Name) == 0 { glog.Warning("Build does not have an Output defined, no output image was pushed to a registry.") } }
// NewEnviromentConfig sets up the initial config from environment variables func NewEnviromentConfig() (*Config, error) { config := NewDefaultConfig() home := os.Getenv("GIT_HOME") if len(home) == 0 { return nil, fmt.Errorf("GIT_HOME is required") } abs, err := filepath.Abs(home) if err != nil { return nil, fmt.Errorf("can't make %q absolute: %v", home, err) } if stat, err := os.Stat(abs); err != nil || !stat.IsDir() { return nil, fmt.Errorf("GIT_HOME must be an existing directory: %v", err) } config.Home = home if publicURL := os.Getenv("PUBLIC_URL"); len(publicURL) > 0 { valid, err := url.Parse(publicURL) if err != nil { return nil, fmt.Errorf("PUBLIC_URL must be a valid URL: %v", err) } config.URL = valid } gitpath := os.Getenv("GIT_PATH") if len(gitpath) == 0 { path, err := exec.LookPath("git") if err != nil { return nil, fmt.Errorf("could not find 'git' in PATH; specify GIT_PATH or set your PATH") } gitpath = path } config.GitBinary = gitpath config.AllowPush = os.Getenv("ALLOW_GIT_PUSH") != "no" config.AllowHooks = os.Getenv("ALLOW_GIT_HOOKS") != "no" config.AllowLazyCreate = os.Getenv("ALLOW_LAZY_CREATE") != "no" if hookpath := os.Getenv("HOOK_PATH"); len(hookpath) != 0 { path, err := filepath.Abs(hookpath) if err != nil { return nil, fmt.Errorf("HOOK_PATH was set but cannot be made absolute: %v", err) } if stat, err := os.Stat(path); err != nil || !stat.IsDir() { return nil, fmt.Errorf("HOOK_PATH must be an existing directory if set: %v", err) } config.HookDirectory = path } if value := os.Getenv("REQUIRE_GIT_AUTH"); len(value) > 0 { parts := strings.Split(value, ":") if len(parts) != 2 { return nil, fmt.Errorf("REQUIRE_GIT_AUTH must be a username and password separated by a ':'") } username, password := parts[0], parts[1] config.AuthenticatorFn = auth.Authenticator(func(info auth.AuthInfo) (bool, error) { if info.Push && !config.AllowPush { return false, nil } if info.Username != username || info.Password != password { return false, nil } return true, nil }) } if value := os.Getenv("GIT_LISTEN"); len(value) > 0 { config.Listen = value } config.CleanBeforeClone = os.Getenv("GIT_FORCE_CLEAN") == "yes" clones := make(map[string]Clone) for _, env := range os.Environ() { if !strings.HasPrefix(env, initialClonePrefix) { continue } parts := strings.SplitN(env, "=", 2) if len(parts) != 2 { continue } key, value := parts[0], parts[1] part := key[len(initialClonePrefix):] if len(part) == 0 { continue } if len(value) == 0 { return nil, fmt.Errorf("%s must not have an empty value", key) } defaultName := strings.Replace(strings.ToLower(part), "_", "-", -1) values := strings.Split(value, ";") var uri, name string switch len(values) { case 1: uri, name = values[0], "" case 2: uri, name = values[0], values[1] if len(name) == 0 { return nil, fmt.Errorf("%s name may not be empty", key) } default: return nil, fmt.Errorf("%s may only have two segments (<url> or <url>;<name>)", key) } url, err := git.ParseRepository(uri) if err != nil { return nil, fmt.Errorf("%s is not a valid repository URI: %v", key, err) } switch url.Scheme { case "http", "https", "git", "ssh": default: return nil, fmt.Errorf("%s %q must be a http, https, git, or ssh URL", key, uri) } if len(name) == 0 { if n, ok := git.NameFromRepositoryURL(url); ok { name = n + ".git" } } if len(name) == 0 { name = defaultName + ".git" } if invalidCloneNameChars.MatchString(name) { return nil, fmt.Errorf("%s name %q must be only letters, numbers, dashes, or underscores", key, name) } if _, ok := reservedNames[name]; ok { return nil, fmt.Errorf("%s name %q is reserved (%v)", key, name, reservedNames) } clones[name] = Clone{ URL: *url, } } config.InitialClones = clones return config, nil }
func (a *AutoLinkBuilds) Link() (map[string]gitserver.Clone, error) { errs := []error{} builders := []*buildapi.BuildConfig{} for _, namespace := range a.Namespaces { list, err := a.Client.BuildConfigs(namespace).List(labels.Everything(), fields.Everything()) if err != nil { errs = append(errs, err) continue } for i := range list.Items { builders = append(builders, &list.Items[i]) } } for _, b := range a.Builders { if hasItem(builders, b) { continue } config, err := a.Client.BuildConfigs(b.Namespace).Get(b.Name) if err != nil { errs = append(errs, err) continue } builders = append(builders, config) } hooks := make(map[string]string) if len(a.PostReceiveHook) > 0 { hooks["post-receive"] = a.PostReceiveHook } clones := make(map[string]gitserver.Clone) for _, builder := range builders { source := builder.Parameters.Source.Git if source == nil { continue } if builder.Annotations == nil { builder.Annotations = make(map[string]string) } // calculate the origin URL uri := source.URI if value, ok := builder.Annotations["git.openshift.io/origin-url"]; ok { uri = value } if len(uri) == 0 { continue } origin, err := git.ParseRepository(uri) if err != nil { errs = append(errs, err) continue } // calculate the local repository name and self URL name := builder.Name if a.CurrentNamespace != builder.Namespace { name = fmt.Sprintf("%s.%s", builder.Namespace, name) } name = fmt.Sprintf("%s.git", name) self := a.LinkFn(name) if self == nil { errs = append(errs, fmt.Errorf("no self URL available, can't update %s", name)) continue } // we can't clone from ourself if self.Host == origin.Host { continue } // update the existing builder changed := false if builder.Annotations["git.openshift.io/origin-url"] != origin.String() { builder.Annotations["git.openshift.io/origin-url"] = origin.String() changed = true } if source.URI != self.String() { source.URI = self.String() changed = true } if changed { if _, err := a.Client.BuildConfigs(builder.Namespace).Update(builder); err != nil { errs = append(errs, err) continue } } clones[name] = gitserver.Clone{ URL: *origin, Hooks: hooks, } } return clones, errors.NewAggregate(errs) }
// NewEnviromentConfig sets up the initial config from environment variables func NewEnviromentConfig() (*Config, error) { config := NewDefaultConfig() home := os.Getenv("GIT_HOME") if len(home) == 0 { return nil, fmt.Errorf("GIT_HOME is required") } abs, err := filepath.Abs(home) if err != nil { return nil, fmt.Errorf("can't make %q absolute: %v", home, err) } if stat, err := os.Stat(abs); err != nil || !stat.IsDir() { return nil, fmt.Errorf("GIT_HOME must be an existing directory: %v", err) } config.Home = home if publicURL := os.Getenv("PUBLIC_URL"); len(publicURL) > 0 { valid, err := url.Parse(publicURL) if err != nil { return nil, fmt.Errorf("PUBLIC_URL must be a valid URL: %v", err) } config.URL = valid } gitpath := os.Getenv("GIT_PATH") if len(gitpath) == 0 { path, err := exec.LookPath("git") if err != nil { return nil, fmt.Errorf("could not find 'git' in PATH; specify GIT_PATH or set your PATH") } gitpath = path } config.GitBinary = gitpath config.AllowPush = os.Getenv("ALLOW_GIT_PUSH") != "no" config.AllowHooks = os.Getenv("ALLOW_GIT_HOOKS") != "no" config.AllowLazyCreate = os.Getenv("ALLOW_LAZY_CREATE") != "no" if hookpath := os.Getenv("HOOK_PATH"); len(hookpath) != 0 { path, err := filepath.Abs(hookpath) if err != nil { return nil, fmt.Errorf("HOOK_PATH was set but cannot be made absolute: %v", err) } if stat, err := os.Stat(path); err != nil || !stat.IsDir() { return nil, fmt.Errorf("HOOK_PATH must be an existing directory if set: %v", err) } config.HookDirectory = path } allowAnonymousGet := os.Getenv("ALLOW_ANON_GIT_PULL") == "yes" serverAuth := os.Getenv("REQUIRE_SERVER_AUTH") gitAuth := os.Getenv("REQUIRE_GIT_AUTH") if len(serverAuth) > 0 && len(gitAuth) > 0 { return nil, fmt.Errorf("only one of REQUIRE_SERVER_AUTH or REQUIRE_GIT_AUTH may be specified") } if len(serverAuth) > 0 { namespace := os.Getenv("AUTH_NAMESPACE") if len(namespace) == 0 { return nil, fmt.Errorf("when REQUIRE_SERVER_AUTH is set, AUTH_NAMESPACE must also be specified") } if serverAuth == "-" { serverAuth = "" } rules := clientcmd.NewDefaultClientConfigLoadingRules() rules.ExplicitPath = serverAuth kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{}) cfg, err := kubeconfig.ClientConfig() if err != nil { return nil, fmt.Errorf("could not create a client for REQUIRE_SERVER_AUTH: %v", err) } osc, err := client.New(cfg) if err != nil { return nil, fmt.Errorf("could not create a client for REQUIRE_SERVER_AUTH: %v", err) } config.AuthMessage = fmt.Sprintf("Authenticating against %s allow-push=%t anon-pull=%t", cfg.Host, config.AllowPush, allowAnonymousGet) config.AuthenticatorFn = auth.Authenticator(func(info auth.AuthInfo) (bool, error) { if !info.Push && allowAnonymousGet { return true, nil } req := &authapi.LocalSubjectAccessReview{ Action: authapi.AuthorizationAttributes{ Verb: "get", Resource: "pods", }, } if info.Push { if !config.AllowPush { return false, nil } req.Action.Verb = "create" } res, err := osc.ImpersonateLocalSubjectAccessReviews(namespace, info.Password).Create(req) if err != nil { if se, ok := err.(*errors.StatusError); ok { return false, &statusError{se} } return false, err } //log.Printf("debug: server response allowed=%t message=%s", res.Allowed, res.Reason) return res.Allowed, nil }) } if len(gitAuth) > 0 { parts := strings.Split(gitAuth, ":") if len(parts) != 2 { return nil, fmt.Errorf("REQUIRE_GIT_AUTH must be a username and password separated by a ':'") } config.AuthMessage = fmt.Sprintf("Authenticating against username/password allow-push=%t", config.AllowPush) username, password := parts[0], parts[1] config.AuthenticatorFn = auth.Authenticator(func(info auth.AuthInfo) (bool, error) { if info.Push { if !config.AllowPush { return false, nil } if allowAnonymousGet { return true, nil } } if info.Username != username || info.Password != password { return false, nil } return true, nil }) } if value := os.Getenv("GIT_LISTEN"); len(value) > 0 { config.Listen = value } config.CleanBeforeClone = os.Getenv("GIT_FORCE_CLEAN") == "yes" clones := make(map[string]Clone) for _, env := range os.Environ() { if !strings.HasPrefix(env, initialClonePrefix) { continue } parts := strings.SplitN(env, "=", 2) if len(parts) != 2 { continue } key, value := parts[0], parts[1] part := key[len(initialClonePrefix):] if len(part) == 0 { continue } if len(value) == 0 { return nil, fmt.Errorf("%s must not have an empty value", key) } defaultName := strings.Replace(strings.ToLower(part), "_", "-", -1) values := strings.Split(value, ";") var uri, name string switch len(values) { case 1: uri, name = values[0], "" case 2: uri, name = values[0], values[1] if len(name) == 0 { return nil, fmt.Errorf("%s name may not be empty", key) } default: return nil, fmt.Errorf("%s may only have two segments (<url> or <url>;<name>)", key) } url, err := git.ParseRepository(uri) if err != nil { return nil, fmt.Errorf("%s is not a valid repository URI: %v", key, err) } switch url.Scheme { case "http", "https", "git", "ssh": default: return nil, fmt.Errorf("%s %q must be a http, https, git, or ssh URL", key, uri) } if len(name) == 0 { if n, ok := git.NameFromRepositoryURL(url); ok { name = n + ".git" } } if len(name) == 0 { name = defaultName + ".git" } if invalidCloneNameChars.MatchString(name) { return nil, fmt.Errorf("%s name %q must be only letters, numbers, dashes, or underscores", key, name) } if _, ok := reservedNames[name]; ok { return nil, fmt.Errorf("%s name %q is reserved (%v)", key, name, reservedNames) } clones[name] = Clone{ URL: *url, } } config.InitialClones = clones return config, nil }