mirror of https://github.com/actions/toolkit
Add specific messages for network-specific node error codes
parent
37a66ebd47
commit
a4bd0f1214
|
@ -7,6 +7,12 @@ import {noopLogs} from './common'
|
||||||
|
|
||||||
jest.mock('@actions/http-client')
|
jest.mock('@actions/http-client')
|
||||||
|
|
||||||
|
const clientOptions = {
|
||||||
|
maxAttempts: 5,
|
||||||
|
retryIntervalMs: 1,
|
||||||
|
retryMultiplier: 1.5
|
||||||
|
}
|
||||||
|
|
||||||
describe('artifact-http-client', () => {
|
describe('artifact-http-client', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
noopLogs()
|
noopLogs()
|
||||||
|
@ -94,11 +100,7 @@ describe('artifact-http-client', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const client = internalArtifactTwirpClient({
|
const client = internalArtifactTwirpClient(clientOptions)
|
||||||
maxAttempts: 5,
|
|
||||||
retryIntervalMs: 1,
|
|
||||||
retryMultiplier: 1.5
|
|
||||||
})
|
|
||||||
const artifact = await client.CreateArtifact({
|
const artifact = await client.CreateArtifact({
|
||||||
workflowRunBackendId: '1234',
|
workflowRunBackendId: '1234',
|
||||||
workflowJobRunBackendId: '5678',
|
workflowJobRunBackendId: '5678',
|
||||||
|
@ -133,11 +135,7 @@ describe('artifact-http-client', () => {
|
||||||
post: mockPost
|
post: mockPost
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const client = internalArtifactTwirpClient({
|
const client = internalArtifactTwirpClient(clientOptions)
|
||||||
maxAttempts: 5,
|
|
||||||
retryIntervalMs: 1,
|
|
||||||
retryMultiplier: 1.5
|
|
||||||
})
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await client.CreateArtifact({
|
await client.CreateArtifact({
|
||||||
workflowRunBackendId: '1234',
|
workflowRunBackendId: '1234',
|
||||||
|
@ -172,11 +170,7 @@ describe('artifact-http-client', () => {
|
||||||
post: mockPost
|
post: mockPost
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const client = internalArtifactTwirpClient({
|
const client = internalArtifactTwirpClient(clientOptions)
|
||||||
maxAttempts: 5,
|
|
||||||
retryIntervalMs: 1,
|
|
||||||
retryMultiplier: 1.5
|
|
||||||
})
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await client.CreateArtifact({
|
await client.CreateArtifact({
|
||||||
workflowRunBackendId: '1234',
|
workflowRunBackendId: '1234',
|
||||||
|
@ -214,11 +208,7 @@ describe('artifact-http-client', () => {
|
||||||
post: mockPost
|
post: mockPost
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const client = internalArtifactTwirpClient({
|
const client = internalArtifactTwirpClient(clientOptions)
|
||||||
maxAttempts: 5,
|
|
||||||
retryIntervalMs: 1,
|
|
||||||
retryMultiplier: 1.5
|
|
||||||
})
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await client.CreateArtifact({
|
await client.CreateArtifact({
|
||||||
workflowRunBackendId: '1234',
|
workflowRunBackendId: '1234',
|
||||||
|
@ -238,4 +228,39 @@ describe('artifact-http-client', () => {
|
||||||
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
||||||
expect(mockPost).toHaveBeenCalledTimes(1)
|
expect(mockPost).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should properly describe a network failure', async () => {
|
||||||
|
class FakeNodeError extends Error {
|
||||||
|
code: string
|
||||||
|
constructor(code: string) {
|
||||||
|
super()
|
||||||
|
this.code = code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockPost = jest.fn(() => {
|
||||||
|
throw new FakeNodeError('ENOTFOUND')
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockHttpClient = (
|
||||||
|
HttpClient as unknown as jest.Mock
|
||||||
|
).mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
post: mockPost
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const client = internalArtifactTwirpClient()
|
||||||
|
await expect(async () => {
|
||||||
|
await client.CreateArtifact({
|
||||||
|
workflowRunBackendId: '1234',
|
||||||
|
workflowJobRunBackendId: '5678',
|
||||||
|
name: 'artifact',
|
||||||
|
version: 4
|
||||||
|
})
|
||||||
|
}).rejects.toThrowError(
|
||||||
|
'Failed to CreateArtifact: Unable to make request: ENOTFOUND\nIf you are using self-hosted runners, please make sure your runner has access to all GitHub endpoints: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github'
|
||||||
|
)
|
||||||
|
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
||||||
|
expect(mockPost).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {info, debug} from '@actions/core'
|
||||||
import {ArtifactServiceClientJSON} from '../../generated'
|
import {ArtifactServiceClientJSON} from '../../generated'
|
||||||
import {getResultsServiceUrl, getRuntimeToken} from './config'
|
import {getResultsServiceUrl, getRuntimeToken} from './config'
|
||||||
import {getUserAgentString} from './user-agent'
|
import {getUserAgentString} from './user-agent'
|
||||||
|
import {NetworkError} from './errors'
|
||||||
|
|
||||||
// The twirp http client must implement this interface
|
// The twirp http client must implement this interface
|
||||||
interface Rpc {
|
interface Rpc {
|
||||||
|
@ -96,6 +97,9 @@ class ArtifactHttpClient implements Rpc {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
isRetryable = true
|
isRetryable = true
|
||||||
errorMessage = error.message
|
errorMessage = error.message
|
||||||
|
if (NetworkError.isNetworkErrorCode(error?.code)) {
|
||||||
|
throw new NetworkError(error?.code)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isRetryable) {
|
if (!isRetryable) {
|
||||||
|
|
|
@ -35,3 +35,25 @@ export class GHESNotSupportedError extends Error {
|
||||||
this.name = 'GHESNotSupportedError'
|
this.name = 'GHESNotSupportedError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class NetworkError extends Error {
|
||||||
|
code: string
|
||||||
|
|
||||||
|
constructor(code: string) {
|
||||||
|
const message = `Unable to make request: ${code}\nIf you are using self-hosted runners, please make sure your runner has access to all GitHub endpoints: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github`
|
||||||
|
super(message)
|
||||||
|
this.code = code
|
||||||
|
this.name = 'NetworkError'
|
||||||
|
}
|
||||||
|
|
||||||
|
static isNetworkErrorCode = (code?: string): boolean => {
|
||||||
|
if (!code) return false
|
||||||
|
return [
|
||||||
|
'ECONNRESET',
|
||||||
|
'ENOTFOUND',
|
||||||
|
'ETIMEDOUT',
|
||||||
|
'ECONNREFUSED',
|
||||||
|
'EHOSTUNREACH'
|
||||||
|
].includes(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {getUploadChunkSize, getConcurrency} from '../shared/config'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import * as stream from 'stream'
|
import * as stream from 'stream'
|
||||||
|
import {NetworkError} from '../shared/errors'
|
||||||
|
|
||||||
export interface BlobUploadResponse {
|
export interface BlobUploadResponse {
|
||||||
/**
|
/**
|
||||||
|
@ -52,12 +53,20 @@ export async function uploadZipToBlobStorage(
|
||||||
|
|
||||||
core.info('Beginning upload of artifact content to blob storage')
|
core.info('Beginning upload of artifact content to blob storage')
|
||||||
|
|
||||||
|
try {
|
||||||
await blockBlobClient.uploadStream(
|
await blockBlobClient.uploadStream(
|
||||||
uploadStream,
|
uploadStream,
|
||||||
bufferSize,
|
bufferSize,
|
||||||
maxConcurrency,
|
maxConcurrency,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (NetworkError.isNetworkErrorCode(error?.code)) {
|
||||||
|
throw new NetworkError(error?.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
core.info('Finished uploading artifact content to blob storage!')
|
core.info('Finished uploading artifact content to blob storage!')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue