@@ -52,16 +52,6 @@ func (a WorkspacesAPI) Create(ws *Workspace, timeout time.Duration) error {
5252 return nil
5353}
5454
55- func dial (hostAndPort , url string , timeout time.Duration ) * resource.RetryError {
56- conn , err := net .DialTimeout ("tcp" , hostAndPort , timeout )
57- if err != nil {
58- return resource .RetryableError (err )
59- }
60- log .Printf ("[INFO] Workspace %s is ready to use" , url )
61- defer conn .Close ()
62- return nil
63- }
64-
6555// generateWorkspaceHostname computes the hostname for the specified workspace,
6656// given the account console hostname.
6757func generateWorkspaceHostname (client * common.DatabricksClient , ws Workspace ) string {
@@ -85,6 +75,42 @@ func generateWorkspaceHostname(client *common.DatabricksClient, ws Workspace) st
8575 return strings .Join (chunks , "." )
8676}
8777
78+ func (a WorkspacesAPI ) verifyWorkspaceReachable (ws Workspace ) * resource.RetryError {
79+ ctx , cancel := context .WithTimeout (a .context , 10 * time .Second )
80+ defer cancel ()
81+ // wait for DNS caches to refresh, as sometimes we cannot make
82+ // API calls to new workspaces immediately after it's created
83+ wsClient := a .client .ClientForHost (ws .WorkspaceURL )
84+ // make a request to Tokens API, just to verify there are no errors
85+ var response map [string ]interface {}
86+ err := wsClient .Get (ctx , "/token/list" , nil , & response )
87+ if apiError , ok := err .(common.APIError ); ok {
88+ err = fmt .Errorf ("workspace %s is not yet reachable: %s" ,
89+ ws .WorkspaceURL , apiError )
90+ log .Printf ("[INFO] %s" , err )
91+ // expected to retry on: dial tcp: lookup XXX: no such host
92+ return resource .RetryableError (err )
93+ }
94+ return nil
95+ }
96+
97+ func (a WorkspacesAPI ) explainWorkspaceFailure (ws Workspace ) error {
98+ if ws .NetworkID == "" {
99+ return fmt .Errorf (ws .WorkspaceStatusMessage )
100+ }
101+ network , nerr := NewNetworksAPI (a .context , a .client ).Read (ws .AccountID , ws .NetworkID )
102+ if nerr != nil {
103+ return fmt .Errorf ("failed to start workspace. Cannot read network: %s" , nerr )
104+ }
105+ var strBuffer bytes.Buffer
106+ for _ , networkHealth := range network .ErrorMessages {
107+ strBuffer .WriteString (fmt .Sprintf ("error: %s;error_msg: %s;" ,
108+ networkHealth .ErrorType , networkHealth .ErrorMessage ))
109+ }
110+ return fmt .Errorf ("Workspace failed to create: %v, network error message: %v" ,
111+ ws .WorkspaceStatusMessage , strBuffer .String ())
112+ }
113+
88114// WaitForRunning will wait until workspace is running, otherwise will try to explain why it failed
89115func (a WorkspacesAPI ) WaitForRunning (ws Workspace , timeout time.Duration ) error {
90116 return resource .RetryContext (a .context , timeout , func () * resource.RetryError {
@@ -94,36 +120,17 @@ func (a WorkspacesAPI) WaitForRunning(ws Workspace, timeout time.Duration) error
94120 }
95121 switch workspace .WorkspaceStatus {
96122 case WorkspaceStatusRunning :
97- // wait for DNS caches to refresh, as sometimes we cannot make
98- // API calls to new workspaces immediately after it's created
99- host := generateWorkspaceHostname (a .client , ws )
100- hostAndPort := fmt .Sprintf ("%s:443" , host )
101- url := fmt .Sprintf ("https://%s" , host )
102123 log .Printf ("[INFO] Workspace is now running" )
103- if strings .Contains (workspace .DeploymentName , "900150983cd24fb0" ) {
124+ if strings .Contains (ws .DeploymentName , "900150983cd24fb0" ) {
104125 // nobody would probably name workspace as 900150983cd24fb0,
105126 // so we'll use it as unit testing shim
106127 return nil
107128 }
108- return dial ( hostAndPort , url , 10 * time . Second )
129+ return a . verifyWorkspaceReachable ( workspace )
109130 case WorkspaceStatusCanceled , WorkspaceStatusFailed :
110131 log .Printf ("[ERROR] Cannot start workspace: %s" , workspace .WorkspaceStatusMessage )
111- if workspace .NetworkID == "" {
112- return resource .NonRetryableError (fmt .Errorf (workspace .WorkspaceStatusMessage ))
113- }
114- network , nerr := NewNetworksAPI (a .context , a .client ).Read (ws .AccountID , ws .NetworkID )
115- if nerr != nil {
116- return resource .NonRetryableError (fmt .Errorf (
117- "failed to start workspace. Cannot read network: %s" , nerr ))
118- }
119- var strBuffer bytes.Buffer
120- for _ , networkHealth := range network .ErrorMessages {
121- strBuffer .WriteString (fmt .Sprintf ("error: %s;error_msg: %s;" ,
122- networkHealth .ErrorType , networkHealth .ErrorMessage ))
123- }
124- return resource .NonRetryableError (fmt .Errorf (
125- "Workspace failed to create: %v, network error message: %v" ,
126- workspace .WorkspaceStatusMessage , strBuffer .String ()))
132+ err = a .explainWorkspaceFailure (workspace )
133+ return resource .NonRetryableError (err )
127134 default :
128135 log .Printf ("[INFO] Workspace %s is %s: %s" , workspace .DeploymentName ,
129136 workspace .WorkspaceStatus , workspace .WorkspaceStatusMessage )
@@ -162,6 +169,11 @@ func (a WorkspacesAPI) Read(mwsAcctID, workspaceID string) (Workspace, error) {
162169 var mwsWorkspace Workspace
163170 workspacesAPIPath := fmt .Sprintf ("/accounts/%s/workspaces/%s" , mwsAcctID , workspaceID )
164171 err := a .client .Get (a .context , workspacesAPIPath , nil , & mwsWorkspace )
172+ if err == nil && mwsWorkspace .WorkspaceURL == "" {
173+ // generate workspace URL based on client's hostname, if response contains no URL
174+ host := generateWorkspaceHostname (a .client , mwsWorkspace )
175+ mwsWorkspace .WorkspaceURL = fmt .Sprintf ("https://%s" , host )
176+ }
165177 return mwsWorkspace , err
166178}
167179
@@ -296,7 +308,6 @@ func ResourceWorkspace() *schema.Resource {
296308 // Default the value of `is_no_public_ip_enabled` because it isn't part of the GET payload.
297309 // The field is only used on creation and we therefore suppress all diffs.
298310 workspace .IsNoPublicIPEnabled = true
299- workspace .WorkspaceURL = fmt .Sprintf ("https://%s" , generateWorkspaceHostname (c , workspace ))
300311 if err = common .StructToData (workspace , workspaceSchema , d ); err != nil {
301312 return err
302313 }
0 commit comments