private async Task CreateContainerInnerAsync(ContainerConfig containerConfig, bool retryDownload) { var containerName = this.activationArgs.ContainerDescription.ContainerName; var containerExist = false; try { var response = await Utility.ExecuteWithRetriesAsync( (operationTimeout) => { return(this.activator.Client.ContainerOperation.CreateContainerAsync( containerName, containerConfig, operationTimeout)); }, $"CreateContainerInnerAsync_{containerName}", TraceType, HostingConfig.Config.DockerRequestTimeout, this.timeoutHelper.RemainingTime); this.containerId = response.ID; HostingTrace.Source.WriteInfo( TraceType, "Creating Container={0} from Image={1} for AppId={2} succeeded.", containerName, containerConfig.Image, this.activationArgs.ContainerDescription.ApplicationId); return; } catch (ContainerApiException ex) { if (ex.StatusCode == HttpStatusCode.NotFound && retryDownload == true) { HostingTrace.Source.WriteInfo( TraceType, "Image not found while creating Container={0} from Image={1} for AppId={2}. StatusCode={3}, ResponseBody={4}.", containerName, containerConfig.Image, activationArgs.ContainerDescription.ApplicationId, ex.StatusCode, ex.ResponseBody); } else if (ex.StatusCode == HttpStatusCode.Conflict) { // // This can happen in case of retries when docker request timeout. // The client connection is close but docker might have just created // the container. // containerExist = true; HostingTrace.Source.WriteInfo( TraceType, "Container already exists while Creating Container={0} from Image={1} for AppId={2}. StatusCode={3}, ResponseBody={4}.", containerName, containerConfig.Image, activationArgs.ContainerDescription.ApplicationId, ex.StatusCode, ex.ResponseBody); } else { var errorMessage = string.Format( "Creating Container from Image={0} failed for {1}.", containerConfig.Image, this.BuildErrorMessage(ex)); this.TraceAndThrow(errorMessage); } } catch (Exception ex) { var errorMessage = string.Format( "Creating Container from Image={0} failed for {1}.", containerConfig.Image, this.BuildErrorMessage(ex)); this.TraceAndThrow(errorMessage); } if (containerExist) { var success = await this.GetContainerIdAsync(containerName, containerConfig); if (!success) { await this.CreateContainerInnerAsync(containerConfig, true); } return; } // // skipCacheCheck=true to handle the case where an image is // deleted outside knowledge of FabricCAS // var imageDescription = new ContainerImageDescription(); imageDescription.ImageName = this.activationArgs.ProcessDescription.ExePath; imageDescription.UseDefaultRepositoryCredentials = this.activationArgs.ContainerDescription.UseDefaultRepositoryCredentials; imageDescription.UseTokenAuthenticationCredentials = this.activationArgs.ContainerDescription.UseTokenAuthenticationCredentials; imageDescription.RepositoryCredential = this.activationArgs.ContainerDescription.RepositoryCredential; await this.activator.ImageDownloader.DownloadImageAsync(imageDescription, HostingConfig.Config.ContainerImageDownloadTimeout, true /*skipCacheCheck*/); await this.CreateContainerInnerAsync(containerConfig, false); }
public async Task DownloadImageAsync(ContainerImageDescription imageDescription, TimeSpan timeout, bool skipCacheCheck = false) { var timeoutHelper = new TimeoutHelper(timeout); var imageTags = imageDescription.ImageName.Split(':'); bool isLatest = false; if (imageTags.Length < 2 || String.IsNullOrEmpty(imageTags[1])) { imageDescription.ImageName = String.Concat(imageDescription.ImageName, ":latest"); isLatest = true; } if (!skipCacheCheck && !isLatest) { if (this.IsImageCached(imageDescription.ImageName)) { HostingTrace.Source.WriteInfo( TraceType, "Container image download skipped as it is cached: ImageName={0}, Time={1}ms .", imageDescription.ImageName, 0); return; } if (await this.IsImagePresentLocallyAsync(imageDescription.ImageName, timeoutHelper.RemainingTime)) { HostingTrace.Source.WriteInfo( TraceType, "Container image download skipped as it is present locally: ImageName={0}, Time={1}ms .", imageDescription.ImageName, 0); this.AddImageToCache(imageDescription.ImageName); return; } } CredentialType currentCredentialAttempt; if (imageDescription.UseTokenAuthenticationCredentials) { currentCredentialAttempt = CredentialType.TokenCredentials; } else if (imageDescription.UseDefaultRepositoryCredentials) { currentCredentialAttempt = CredentialType.ClusterManifestDefaultCredentials; } else { currentCredentialAttempt = CredentialType.AppManifestCredentials; } try { AuthConfig authConfig = this.GetAuthConfig(imageDescription.RepositoryCredential, imageDescription.ImageName, currentCredentialAttempt); var downloadProgressStream = await Utility.ExecuteWithRetriesAsync( (operationTimeout) => { return(this.ImageOperation.CreateImageAsync( imageDescription.ImageName, authConfig, operationTimeout)); }, $"CreateImageAsync_{imageDescription.ImageName}", TraceType, HostingConfig.Config.DockerRequestTimeout, timeoutHelper.RemainingTime); await this.TrackDownloadProgressAsync( imageDescription.ImageName, downloadProgressStream, timeoutHelper.RemainingTime); if (!await this.IsImagePresentLocallyAsync(imageDescription.ImageName, timeoutHelper.RemainingTime)) { throw new Exception( $"Container image download failed - ImageName={imageDescription.ImageName}. " + $"Please check if container image OS is compatible with host OS or if you have sufficient disk space on the machine. " + $"Try to pull the image manually on the machine using docker CLI for further error details. "); } this.AddImageToCache(imageDescription.ImageName); } catch (ContainerApiException dex) { var errMsgBuilder = new StringBuilder(); errMsgBuilder.AppendFormat( "Container image download failed with authorization attempt type {0} for ImageName={1}. DockerRequest returned StatusCode={2} with ResponseBody={3}.", currentCredentialAttempt.ToString(), imageDescription.ImageName, dex.StatusCode, dex.ResponseBody); if (dex.StatusCode == HttpStatusCode.NotFound) { errMsgBuilder.Append( "Please check if image is present in repository or repository credentials provided are correct."); } HostingTrace.Source.WriteWarning(TraceType, errMsgBuilder.ToString()); throw new FabricException(errMsgBuilder.ToString(), FabricErrorCode.InvalidOperation); } catch (Exception ex) { var errMsgBuilder = new StringBuilder(); errMsgBuilder.AppendFormat( "Container image download failed with authorization attempt type {0} for ImageName={1} with unexpected error. Exception={2}.", currentCredentialAttempt.ToString(), imageDescription.ImageName, ex.ToString()); HostingTrace.Source.WriteWarning(TraceType, errMsgBuilder.ToString()); throw new FabricException(errMsgBuilder.ToString(), FabricErrorCode.InvalidOperation); } }