How to make programmatic image upload generate thumbnail and sizes?

In order for wp_generate_attachment_metadata() to work properly in that it creates a thumbnail and other intermediate/image sizes like medium and large, the image attachment/post data must have the proper MIME type (post_mime_type), and despite you did set it in your $attachment array (the post data), there’s a problem with this part in your code:

$wp_filetype = wp_check_filetype(basename($filename), null ); 

where $filename was defined as $filename="jana-eisenstein"; which is a file name without the extension (e.g. .png), and the value of that variable remained unchanged in your function, so with the above code, both ext and type in the $wp_filetype array are false.

Therefore, the 'post_mime_type' => $wp_filetype['type'] is equivalent to 'post_mime_type' => false, causing the image attachment to having invalid MIME type.

And to fix it, you can either pass the full file path to wp_check_filetype(), or use basename( $uploadfile ) and not basename( $filename ):

$wp_filetype = wp_check_filetype( $uploadfile, null ); // pass full path
$wp_filetype = wp_check_filetype( basename( $uploadfile ), null ); // OR this works, too

Alternate Option

This is based on this example which uses media_handle_sideload() and automatically calls wp_update_attachment_metadata(), so you would not need to manually call it. 🙂

function add_remote_image_to_library( $source_url, $filename, $img_title ) {
    require_once ABSPATH . 'wp-admin/includes/image.php';
    require_once ABSPATH . 'wp-admin/includes/media.php'; // load media_handle_sideload()
    require_once ABSPATH . 'wp-admin/includes/file.php';  // load download_url()

    // Download the file to a temporary (system) file.
    $tmp = download_url( $source_url );

    // Build an array like in the $_FILES superglobal.
    $wp_filetype = wp_check_filetype( basename( $source_url ) );
    $file_array = array(
        'name'     => $filename . '.' . $wp_filetype['ext'],
        'tmp_name' => $tmp,
    );

    // Must manually delete the file in case of download errors.
    if ( is_wp_error( $tmp ) ) {
        @unlink( $file_array[ 'tmp_name' ] );
        return $tmp;
    }

    // Now create the attachment plus thumbnails and other sizes.
    $post_id = 0; // the parent post ID, if applicable
    $id = media_handle_sideload( $file_array, $post_id, null, array(
        'post_title' => $img_title,
    ) );

    // You should delete the temporary file if the above resulted in a WP_Error.
    if ( is_wp_error( $id ) ) {
        @unlink( $file_array['tmp_name'] );
        return $id;
    }

    return $id; // return the attachment ID or whatever that you want to return..
}