|
1 | 1 | package cli_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | +"bufio" |
| 5 | +"bytes" |
4 | 6 | "context"
|
5 | 7 | "fmt"
|
6 | 8 | "io"
|
@@ -692,3 +694,152 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
|
692 | 694 | })
|
693 | 695 | }
|
694 | 696 | }
|
| 697 | + |
| 698 | +funcTestConfigSSH_Hostnames(t*testing.T) { |
| 699 | +t.Parallel() |
| 700 | + |
| 701 | +typeresourceSpecstruct { |
| 702 | +namestring |
| 703 | +agents []string |
| 704 | +} |
| 705 | +tests:= []struct { |
| 706 | +namestring |
| 707 | +resources []resourceSpec |
| 708 | +expected []string |
| 709 | +}{ |
| 710 | +{ |
| 711 | +name:"one resource with one agent", |
| 712 | +resources: []resourceSpec{ |
| 713 | +{name:"foo",agents: []string{"agent1"}}, |
| 714 | +}, |
| 715 | +expected: []string{"coder.@","coder.@.agent1"}, |
| 716 | +}, |
| 717 | +{ |
| 718 | +name:"one resource with two agents", |
| 719 | +resources: []resourceSpec{ |
| 720 | +{name:"foo",agents: []string{"agent1","agent2"}}, |
| 721 | +}, |
| 722 | +expected: []string{"coder.@.agent1","coder.@.agent2"}, |
| 723 | +}, |
| 724 | +{ |
| 725 | +name:"two resources with one agent", |
| 726 | +resources: []resourceSpec{ |
| 727 | +{name:"foo",agents: []string{"agent1"}}, |
| 728 | +{name:"bar"}, |
| 729 | +}, |
| 730 | +expected: []string{"coder.@","coder.@.agent1"}, |
| 731 | +}, |
| 732 | +{ |
| 733 | +name:"two resources with two agents", |
| 734 | +resources: []resourceSpec{ |
| 735 | +{name:"foo",agents: []string{"agent1"}}, |
| 736 | +{name:"bar",agents: []string{"agent2"}}, |
| 737 | +}, |
| 738 | +expected: []string{"coder.@.agent1","coder.@.agent2"}, |
| 739 | +}, |
| 740 | +} |
| 741 | + |
| 742 | +for_,tt:=rangetests { |
| 743 | +tt:=tt |
| 744 | +t.Run(tt.name,func(t*testing.T) { |
| 745 | +t.Parallel() |
| 746 | + |
| 747 | +varresources []*proto.Resource |
| 748 | +for_,resourceSpec:=rangett.resources { |
| 749 | +resource:=&proto.Resource{ |
| 750 | +Name:resourceSpec.name, |
| 751 | +Type:"aws_instance", |
| 752 | +} |
| 753 | +for_,agentName:=rangeresourceSpec.agents { |
| 754 | +resource.Agents=append(resource.Agents,&proto.Agent{ |
| 755 | +Id:uuid.NewString(), |
| 756 | +Name:agentName, |
| 757 | +}) |
| 758 | +} |
| 759 | +resources=append(resources,resource) |
| 760 | +} |
| 761 | + |
| 762 | +provisionResponse:= []*proto.Provision_Response{{ |
| 763 | +Type:&proto.Provision_Response_Complete{ |
| 764 | +Complete:&proto.Provision_Complete{ |
| 765 | +Resources:resources, |
| 766 | +}, |
| 767 | +}, |
| 768 | +}} |
| 769 | + |
| 770 | +client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerD:true}) |
| 771 | +user:=coderdtest.CreateFirstUser(t,client) |
| 772 | +// authToken := uuid.NewString() |
| 773 | +version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{ |
| 774 | +Parse:echo.ParseComplete, |
| 775 | +ProvisionDryRun:provisionResponse, |
| 776 | +Provision:provisionResponse, |
| 777 | +}) |
| 778 | +coderdtest.AwaitTemplateVersionJob(t,client,version.ID) |
| 779 | +template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID) |
| 780 | +workspace:=coderdtest.CreateWorkspace(t,client,user.OrganizationID,template.ID) |
| 781 | +coderdtest.AwaitWorkspaceBuildJob(t,client,workspace.LatestBuild.ID) |
| 782 | + |
| 783 | +sshConfigFile,_:=sshConfigFileNames(t) |
| 784 | + |
| 785 | +cmd,root:=clitest.New(t,"config-ssh","--ssh-config-file",sshConfigFile) |
| 786 | +clitest.SetupConfig(t,client,root) |
| 787 | +doneChan:=make(chanstruct{}) |
| 788 | +pty:=ptytest.New(t) |
| 789 | +cmd.SetIn(pty.Input()) |
| 790 | +cmd.SetOut(pty.Output()) |
| 791 | +gofunc() { |
| 792 | +deferclose(doneChan) |
| 793 | +err:=cmd.Execute() |
| 794 | +assert.NoError(t,err) |
| 795 | +}() |
| 796 | + |
| 797 | +matches:= []struct { |
| 798 | +match,writestring |
| 799 | +}{ |
| 800 | +{match:"Continue?",write:"yes"}, |
| 801 | +} |
| 802 | +for_,m:=rangematches { |
| 803 | +pty.ExpectMatch(m.match) |
| 804 | +pty.WriteLine(m.write) |
| 805 | +} |
| 806 | + |
| 807 | +<-doneChan |
| 808 | + |
| 809 | +varexpectedHosts []string |
| 810 | +for_,hostnamePattern:=rangett.expected { |
| 811 | +hostname:=strings.ReplaceAll(hostnamePattern,"@",workspace.Name) |
| 812 | +expectedHosts=append(expectedHosts,hostname) |
| 813 | +} |
| 814 | + |
| 815 | +hosts:=sshConfigFileParseHosts(t,sshConfigFile) |
| 816 | +require.ElementsMatch(t,expectedHosts,hosts) |
| 817 | +}) |
| 818 | +} |
| 819 | +} |
| 820 | + |
| 821 | +// sshConfigFileParseHosts reads a file in the format of .ssh/config and extracts |
| 822 | +// the hostnames that are listed in "Host" directives. |
| 823 | +funcsshConfigFileParseHosts(t*testing.T,namestring) []string { |
| 824 | +t.Helper() |
| 825 | +b,err:=os.ReadFile(name) |
| 826 | +require.NoError(t,err) |
| 827 | + |
| 828 | +varresult []string |
| 829 | +lineScanner:=bufio.NewScanner(bytes.NewBuffer(b)) |
| 830 | +forlineScanner.Scan() { |
| 831 | +line:=lineScanner.Text() |
| 832 | +line=strings.TrimSpace(line) |
| 833 | + |
| 834 | +tokenScanner:=bufio.NewScanner(bytes.NewBufferString(line)) |
| 835 | +tokenScanner.Split(bufio.ScanWords) |
| 836 | +ok:=tokenScanner.Scan() |
| 837 | +ifok&&tokenScanner.Text()=="Host" { |
| 838 | +fortokenScanner.Scan() { |
| 839 | +result=append(result,tokenScanner.Text()) |
| 840 | +} |
| 841 | +} |
| 842 | +} |
| 843 | + |
| 844 | +returnresult |
| 845 | +} |