From 9d5b20753967c88bd76f55ecbc37e854bd3a63ce Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Wed, 19 Dec 2018 17:43:17 +0100 Subject: WIP: Parse commands & enable features --- go/cmd/gitlab-shell/command/command.go | 92 +++++++++++++++++++++++ go/cmd/gitlab-shell/main.go | 42 +++++++++-- go/internal/config/config.go | 4 +- go/internal/gitlabclient/client.go | 14 +--- spec/gitlab_shell_gitlab_shell_spec.rb | 132 ++++++++++++++++++++------------- 5 files changed, 214 insertions(+), 70 deletions(-) create mode 100644 go/cmd/gitlab-shell/command/command.go diff --git a/go/cmd/gitlab-shell/command/command.go b/go/cmd/gitlab-shell/command/command.go new file mode 100644 index 0000000..c35a685 --- /dev/null +++ b/go/cmd/gitlab-shell/command/command.go @@ -0,0 +1,92 @@ +package command + +import ( + "os" + "regexp" + "strconv" +) + +type CommandType string + +const ( + Discover CommandType = "discover" +) + +type Command struct { + GitlabUsername string + GitlabKeyId int + SshConnection bool + Command string + Type CommandType +} + +func New(arguments []string) (*Command, error) { + _, sshConnection := os.LookupEnv("SSH_CONNECTION") + + command := &Command{SshConnection: sshConnection} + + if _, err := parseWho(arguments, command); err != nil { + return nil, err + } + + originalCommand, _ := os.LookupEnv("SSH_ORIGINAL_COMMAND") + parseCommand(originalCommand, command) + + return command, nil +} + +func parseWho(arguments []string, command *Command) (*Command, error) { + var err error = nil + + for _, argument := range arguments { + keyId, err := tryParseKeyId(argument) + if keyId > 0 && err != nil { + command.GitlabKeyId = keyId + break + } + + username, err := tryParseUsername(argument) + if username != "" && err != nil { + command.GitlabUsername = username + break + } + } + + return command, err +} + +func tryParseKeyId(argument string) (int, error) { + whoKeyRegex, err := regexp.Compile(`\bkey-(?P\d+)\b`) + if err != nil { + return 0, err + } + + keyMatch := whoKeyRegex.FindString(argument) + if keyMatch != "" { + gitlabKeyId, err := strconv.Atoi(keyMatch) + + return gitlabKeyId, err + } + + return 0, nil +} + +func tryParseUsername(argument string) (string, error) { + whoUsernameRegex, err := regexp.Compile(`\busername-(?P\S+)\b`) + if err != nil { + return "", err + } + + usernameMatch := whoUsernameRegex.FindString(argument) + return usernameMatch, nil +} + +func parseCommand(commandString string, command *Command) *Command { + command.Command = commandString + + if commandString == "" { + command.Type = Discover + } + + return nil +} diff --git a/go/cmd/gitlab-shell/main.go b/go/cmd/gitlab-shell/main.go index ae54151..331e93d 100644 --- a/go/cmd/gitlab-shell/main.go +++ b/go/cmd/gitlab-shell/main.go @@ -6,22 +6,36 @@ import ( "path/filepath" "syscall" + "gitlab.com/gitlab-org/gitlab-shell/go/cmd/gitlab-shell/command" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" ) var ( - binDir string - rootDir string + binDir string + rootDir string + features map[command.CommandType]bool ) func init() { binDir = filepath.Dir(os.Args[0]) rootDir = filepath.Dir(binDir) + features = map[command.CommandType]bool{} } -func migrate(*config.Config) (int, bool) { - // TODO: Dispatch appropriate requests to Go handlers and return - // depending on how they fare +func migrate(config *config.Config) (int, bool) { + if !config.Migration.Enabled { + return 0, false + } + command, err := command.New(os.Args) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to build command: %v\n", err) + return 0, false + } + + if featureEnabled(config, command.Type) { + + } + return 0, false } @@ -42,7 +56,7 @@ func main() { // warning as this isn't something we can sustain indefinitely config, err := config.NewFromDir(rootDir) if err != nil { - fmt.Fprintln(os.Stderr, "Failed to read config, falling back to gitlab-shell-ruby") + fmt.Fprintf(os.Stderr, "Failed to read config, falling back to gitlab-shell-ruby: %v", err) execRuby() } @@ -54,3 +68,19 @@ func main() { // Since a migration has not handled the command, fall back to Ruby to do so execRuby() } + +func featureEnabled(config *config.Config, commandType command.CommandType) bool { + if features[commandType] { + return true + } + + fmt.Fprintf(os.Stderr, "Config: %v", config.Migration) + + for _, featureName := range config.Migration.Features { + fmt.Fprintf(os.Stderr, "Setting feature: %v to %v", featureName, command.CommandType(featureName)) + + features[command.CommandType(featureName)] = true + } + + return features[commandType] +} diff --git a/go/internal/config/config.go b/go/internal/config/config.go index d325fd4..76ba07d 100644 --- a/go/internal/config/config.go +++ b/go/internal/config/config.go @@ -74,7 +74,9 @@ func parseConfig(configBytes []byte, cfg *Config) error { cfg.LogFormat = "text" } - baseUrl, err := url.Parse(cfg.GitlabUrl) + unescapedUrl, err := url.PathUnescape(cfg.GitlabUrl) + + baseUrl, err := url.Parse(unescapedUrl) if err != nil { return err } diff --git a/go/internal/gitlabclient/client.go b/go/internal/gitlabclient/client.go index c186b6c..5a8dd9d 100644 --- a/go/internal/gitlabclient/client.go +++ b/go/internal/gitlabclient/client.go @@ -4,7 +4,7 @@ import ( "net/http" "time" - "gitlab.com/gitlab-org/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" ) type Client struct { @@ -21,20 +21,12 @@ func New() (*Client, error) { return nil, err } - tr = &http.Transport{ + tr := &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableCompression: true, } - httpClient = &http.Client{Transport: tr} + httpClient := &http.Client{Transport: tr} return &Client{config: config, httpClient: httpClient}, nil } - -func (c *Client) Discover(gitlabId string) { - -} - -func (c *Client) get(path string) (*Response, error) { - -} diff --git a/spec/gitlab_shell_gitlab_shell_spec.rb b/spec/gitlab_shell_gitlab_shell_spec.rb index 9afeac8..398b50f 100644 --- a/spec/gitlab_shell_gitlab_shell_spec.rb +++ b/spec/gitlab_shell_gitlab_shell_spec.rb @@ -43,11 +43,7 @@ describe 'bin/gitlab-shell' do sleep(0.1) while @webrick_thread.alive? && @server.status != :Running raise "Couldn't start stub GitlabNet server" unless @server.status == :Running - - File.open(config_path, 'w') do |f| - f.write("---\ngitlab_url: http+unix://#{CGI.escape(tmp_socket_path)}\n") - end - + system(original_root_path, 'bin/compile') copy_dirs = ['bin', 'lib'] FileUtils.rm_rf(copy_dirs.map { |d| File.join(tmp_root_path, d) }) FileUtils.cp_r(copy_dirs, tmp_root_path) @@ -61,72 +57,98 @@ describe 'bin/gitlab-shell' do let(:gitlab_shell_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell') } - # Basic valid input - it 'succeeds and prints username when a valid known key id is given' do - output, status = run!(["key-100"]) + shared_examples 'results with keys' do + # Basic valid input + it 'succeeds and prints username when a valid known key id is given' do + output, status = run!(["key-100"]) - expect(output).to eq("Welcome to GitLab, @someuser!\n") - expect(status).to be_success - end + expect(output).to eq("Welcome to GitLab, @someuser!\n") + expect(status).to be_success + end - it 'succeeds and prints username when a valid known username is given' do - output, status = run!(["username-someuser"]) + it 'succeeds and prints username when a valid known username is given' do + output, status = run!(["username-someuser"]) - expect(output).to eq("Welcome to GitLab, @someuser!\n") - expect(status).to be_success - end + expect(output).to eq("Welcome to GitLab, @someuser!\n") + expect(status).to be_success + end - # Valid but unknown input - it 'succeeds and prints Anonymous when a valid unknown key id is given' do - output, status = run!(["key-12345"]) + # Valid but unknown input + it 'succeeds and prints Anonymous when a valid unknown key id is given' do + output, status = run!(["key-12345"]) - expect(output).to eq("Welcome to GitLab, Anonymous!\n") - expect(status).to be_success - end + expect(output).to eq("Welcome to GitLab, Anonymous!\n") + expect(status).to be_success + end - it 'succeeds and prints Anonymous when a valid unknown username is given' do - output, status = run!(["username-unknown"]) + it 'succeeds and prints Anonymous when a valid unknown username is given' do + output, status = run!(["username-unknown"]) - expect(output).to eq("Welcome to GitLab, Anonymous!\n") - expect(status).to be_success - end + expect(output).to eq("Welcome to GitLab, Anonymous!\n") + expect(status).to be_success + end - # Invalid input. TODO: capture stderr & compare - it 'gets an ArgumentError on invalid input (empty)' do - output, status = run!([]) + # Invalid input. TODO: capture stderr & compare + it 'gets an ArgumentError on invalid input (empty)' do + output, status = run!([]) - expect(output).to eq("") - expect(status).not_to be_success - end + expect(output).to eq("") + expect(status).not_to be_success + end - it 'gets an ArgumentError on invalid input (unknown)' do - output, status = run!(["whatever"]) + it 'gets an ArgumentError on invalid input (unknown)' do + output, status = run!(["whatever"]) - expect(output).to eq("") - expect(status).not_to be_success - end + expect(output).to eq("") + expect(status).not_to be_success + end - it 'gets an ArgumentError on invalid input (multiple unknown)' do - output, status = run!(["this", "is", "all", "invalid"]) + it 'gets an ArgumentError on invalid input (multiple unknown)' do + output, status = run!(["this", "is", "all", "invalid"]) - expect(output).to eq("") - expect(status).not_to be_success + expect(output).to eq("") + expect(status).not_to be_success + end + + # Not so basic valid input + # (https://gitlab.com/gitlab-org/gitlab-shell/issues/145) + it 'succeeds and prints username when a valid known key id is given in the middle of other input' do + output, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "key-100", "2foo"]) + + expect(output).to eq("Welcome to GitLab, @someuser!\n") + expect(status).to be_success + end + + it 'succeeds and prints username when a valid known username is given in the middle of other input' do + output, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "username-someuser" ,"foo"]) + + expect(output).to eq("Welcome to GitLab, @someuser!\n") + expect(status).to be_success + end end - # Not so basic valid input - # (https://gitlab.com/gitlab-org/gitlab-shell/issues/145) - it 'succeeds and prints username when a valid known key id is given in the middle of other input' do - output, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "key-100", "2foo"]) + describe 'without go features' do + before(:context) do + write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}") + end - expect(output).to eq("Welcome to GitLab, @someuser!\n") - expect(status).to be_success + it_behaves_like 'results with keys' end - it 'succeeds and prints username when a valid known username is given in the middle of other input' do - output, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "username-someuser" ,"foo"]) + describe 'with the go discover feature' do + before(:context) do + write_config( + "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}", + "migration" => { "enabled" => true, + "features" => ["discover"] } + ) + end + + it 'writes the correct config' do + puts File.read(config_path) + end - expect(output).to eq("Welcome to GitLab, @someuser!\n") - expect(status).to be_success + it_behaves_like 'results with keys' end def run!(args) @@ -139,4 +161,10 @@ describe 'bin/gitlab-shell' do [output, $?] end + + def write_config(config) + File.open(config_path, 'w') do |f| + f.write(config.to_yaml) + end + end end -- cgit v1.2.1